From e5290641d50566a9aff8eae9bec8d585f79077de Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sat, 25 Feb 2006 02:04:42 +0000 Subject: Synched with documentation git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@228 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/build/pjmedia.dsp | 12 +- pjmedia/include/pjmedia/jbuf.h | 3 + pjmedia/include/pjmedia/silencedet.h | 120 ++++++++++++++++ pjmedia/include/pjmedia/vad.h | 120 ---------------- pjmedia/src/pjmedia/conference.c | 18 +-- pjmedia/src/pjmedia/jbuf.c | 3 + pjmedia/src/pjmedia/silencedet.c | 269 +++++++++++++++++++++++++++++++++++ pjmedia/src/pjmedia/stream.c | 19 ++- pjmedia/src/pjmedia/vad.c | 269 ----------------------------------- 9 files changed, 425 insertions(+), 408 deletions(-) create mode 100644 pjmedia/include/pjmedia/silencedet.h delete mode 100644 pjmedia/include/pjmedia/vad.h create mode 100644 pjmedia/src/pjmedia/silencedet.c delete mode 100644 pjmedia/src/pjmedia/vad.c (limited to 'pjmedia') diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index 963a85b8..7edaa90e 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -161,11 +161,11 @@ SOURCE=..\src\pjmedia\session.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\stream.c +SOURCE=..\src\pjmedia\silencedet.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\vad.c +SOURCE=..\src\pjmedia\stream.c # End Source File # End Group # Begin Group "Header Files" @@ -237,19 +237,19 @@ SOURCE=..\include\pjmedia\session.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\sound.h +SOURCE=..\include\pjmedia\silencedet.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\stream.h +SOURCE=..\include\pjmedia\sound.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\types.h +SOURCE=..\include\pjmedia\stream.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\vad.h +SOURCE=..\include\pjmedia\types.h # End Source File # Begin Source File diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index d4cfebcc..9a0d5c6a 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -16,6 +16,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * Based on implementation kindly contributed by Switchlab, Ltd. + */ #ifndef __PJMEDIA_JBUF_H__ #define __PJMEDIA_JBUF_H__ diff --git a/pjmedia/include/pjmedia/silencedet.h b/pjmedia/include/pjmedia/silencedet.h new file mode 100644 index 00000000..91da8597 --- /dev/null +++ b/pjmedia/include/pjmedia/silencedet.h @@ -0,0 +1,120 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 + */ +#ifndef __PJMEDIA_SILENCE_DET_H__ +#define __PJMEDIA_SILENCE_DET_H__ + + +/** + * @file silencedet.h + * @brief Adaptive silence detector. + */ +#include + +PJ_BEGIN_DECL + + +/** + * Opaque declaration for silence detector. + */ +typedef struct pjmedia_silence_det pjmedia_silence_det; + + +/** + * Create voice activity detector with default settings. The default settings + * are to perform adaptive silence detection, which adjusts the noise level + * dynamically based on current input level. + * + * @param pool Pool for allocating the structure. + * @param p_sd Pointer to receive the silence detector instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_silence_det_create( pj_pool_t *pool, + pjmedia_silence_det **p_sd ); + + +/** + * Set the sd to operate in adaptive mode. + * + * @param sd The silence detector + * @param frame_size Number of samples per frame. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_silence_det_set_adaptive( pjmedia_silence_det *sd, + unsigned frame_size); + + +/** + * Set the sd to operate in fixed threshold mode. + * + * @param sd The silence detector + * @param frame_size Number of samplse per frame. + * @param threshold The silence threshold. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_silence_det_set_fixed( pjmedia_silence_det *sd, + unsigned frame_size, + unsigned threshold ); + +/** + * Disable the silence detector. + * + * @param sd The silence detector + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_silence_det_disable( pjmedia_silence_det *sd ); + + +/** + * Calculate average signal level for the given samples. + * + * @param samples Pointer to 16-bit PCM samples. + * @param count Number of samples in the input. + * + * @return The average signal level, which simply is total level + * divided by number of samples. + */ +PJ_DECL(pj_int32_t) pjmedia_silence_det_calc_avg_signal( const pj_int16_t samples[], + pj_size_t count ); + + +/** + * Perform voice activity detection on the given input samples. + * + * @param sd The silence detector instance. + * @param samples Pointer to 16-bit PCM input samples. + * @param count Number of samples in the input. + * @param p_level Optional pointer to receive average signal level + * of the input samples. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_bool_t) pjmedia_silence_det_detect_silence( pjmedia_silence_det *sd, + const pj_int16_t samples[], + pj_size_t count, + pj_int32_t *p_level); + + +PJ_END_DECL + +#endif /* __PJMEDIA_SILENCE_DET_H__ */ + diff --git a/pjmedia/include/pjmedia/vad.h b/pjmedia/include/pjmedia/vad.h deleted file mode 100644 index fc7f3b2f..00000000 --- a/pjmedia/include/pjmedia/vad.h +++ /dev/null @@ -1,120 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 - */ -#ifndef __PJMEDIA_VAD_H__ -#define __PJMEDIA_VAD_H__ - - -/** - * @file vad.h - * @brief Simple, adaptive silence detector. - */ -#include - -PJ_BEGIN_DECL - - -/** - * @see pjmedia_vad - */ -typedef struct pjmedia_vad pjmedia_vad; - - -/** - * Create voice activity detector with default settings. The default settings - * are to perform adaptive silence detection, which adjusts the noise level - * dynamically based on current input level. - * - * @param pool Pool for allocating the structure. - * @param p_vad Pointer to receive the VAD instance. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_vad_create( pj_pool_t *pool, - pjmedia_vad **p_vad ); - - -/** - * Set the vad to operate in adaptive mode. - * - * @param vad The vad - * @param frame_size Number of samplse per frame. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_vad_set_adaptive( pjmedia_vad *vad, - unsigned frame_size); - - -/** - * Set the vad to operate in fixed threshold mode. - * - * @param vad The vad - * @param frame_size Number of samplse per frame. - * @param threshold The silence threshold. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_vad_set_fixed( pjmedia_vad *vad, - unsigned frame_size, - unsigned threshold ); - -/** - * Disable the vad. - * - * @param vad The vad - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_vad_disable( pjmedia_vad *vad ); - - -/** - * Calculate average signal level for the given samples. - * - * @param samples Pointer to 16-bit PCM samples. - * @param count Number of samples in the input. - * - * @return The average signal level, which simply is total level - * divided by number of samples. - */ -PJ_DECL(pj_int32_t) pjmedia_vad_calc_avg_signal( const pj_int16_t samples[], - pj_size_t count ); - - -/** - * Perform voice activity detection on the given input samples. - * - * @param vad The VAD instance. - * @param samples Pointer to 16-bit PCM input samples. - * @param count Number of samples in the input. - * @param p_level Optional pointer to receive average signal level - * of the input samples. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_bool_t) pjmedia_vad_detect_silence( pjmedia_vad *vad, - const pj_int16_t samples[], - pj_size_t count, - pj_int32_t *p_level); - - -PJ_END_DECL - -#endif /* __PJMEDIA_VAD_H__ */ - diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index ba29a602..0db3ccad 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include +#include #include #include #include @@ -55,7 +55,7 @@ struct conf_port pjmedia_port_op tx_setting; /**< Can we transmit to this port */ int listener_cnt; /**< Number of listeners. */ pj_bool_t *listeners; /**< Array of listeners. */ - pjmedia_vad *vad; /**< VAD for this port. */ + pjmedia_silence_det *vad; /**< VAD for this port. */ /* Tx buffer contains the frame to be "transmitted" to this port * (i.e. for put_frame()). @@ -139,11 +139,11 @@ static pj_status_t create_conf_port( pj_pool_t *pool, /* Create and init vad. */ - status = pjmedia_vad_create( pool, &conf_port->vad); + status = pjmedia_silence_det_create( pool, &conf_port->vad); if (status != PJ_SUCCESS) return status; - pjmedia_vad_set_adaptive(conf_port->vad, conf->samples_per_frame); + pjmedia_silence_det_set_adaptive(conf_port->vad, conf->samples_per_frame); /* Create TX buffers. */ @@ -738,10 +738,10 @@ static pj_status_t play_cb( /* in */ void *user_data, continue; /* Do we have signal? */ - silence = pjmedia_vad_detect_silence(conf_port->vad, - output, - conf->samples_per_frame, - &level); + silence = pjmedia_silence_det_detect_silence(conf_port->vad, + output, + conf->samples_per_frame, + &level); /* Skip if we don't have signal. */ if (silence) { @@ -789,7 +789,7 @@ static pj_status_t play_cb( /* in */ void *user_data, frame.type = PJMEDIA_FRAME_TYPE_NONE; frame.buf = NULL; frame.size = 0; - + if (conf_port->port) pjmedia_port_put_frame(conf_port->port, &frame); diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 436585db..e40b1ef5 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -16,6 +16,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * Based on implementation kindly contributed by Switchlab, Ltd. + */ #include #include #include diff --git a/pjmedia/src/pjmedia/silencedet.c b/pjmedia/src/pjmedia/silencedet.c new file mode 100644 index 00000000..2c7583b0 --- /dev/null +++ b/pjmedia/src/pjmedia/silencedet.c @@ -0,0 +1,269 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 +#include +#include +#include +#include + + +#define THIS_FILE "silencedet.c" + +typedef enum pjmedia_silence_det_mode { + VAD_MODE_NONE, + VAD_MODE_FIXED, + VAD_MODE_ADAPTIVE +} pjmedia_silence_det_mode; + + +/** + * This structure holds the silence detector state. + */ +struct pjmedia_silence_det +{ + int mode; /**< VAD mode. */ + unsigned frame_size; /**< Samples per frame. */ + + + unsigned min_signal_cnt; /**< # of signal frames.before talk burst */ + unsigned min_silence_cnt; /**< # of silence frames before silence. */ + unsigned recalc_cnt; /**< # of frames before adaptive recalc. */ + + pj_bool_t in_talk; /**< In talk burst? */ + unsigned cur_cnt; /**< # of frames in current mode. */ + unsigned signal_cnt; /**< # of signal frames received. */ + unsigned silence_cnt; /**< # of silence frames received */ + unsigned cur_threshold; /**< Current silence threshold. */ + unsigned weakest_signal; /**< Weakest signal detected. */ + unsigned loudest_silence; /**< Loudest silence detected. */ +}; + + + +unsigned char linear2ulaw(int pcm_val); + + +PJ_DEF(pj_status_t) pjmedia_silence_det_create( pj_pool_t *pool, + pjmedia_silence_det **p_sd) +{ + pjmedia_silence_det *sd; + + PJ_ASSERT_RETURN(pool && p_sd, PJ_EINVAL); + + sd = pj_pool_zalloc(pool, sizeof(struct pjmedia_silence_det)); + + sd->weakest_signal = 0xFFFFFFFFUL; + sd->loudest_silence = 0; + sd->signal_cnt = 0; + sd->silence_cnt = 0; + + /* Restart in adaptive, silent mode */ + sd->in_talk = PJ_FALSE; + pjmedia_silence_det_set_adaptive( sd, 160 ); + + *p_sd = sd; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_silence_det_set_adaptive( pjmedia_silence_det *sd, + unsigned frame_size) +{ + PJ_ASSERT_RETURN(sd && frame_size, PJ_EINVAL); + + sd->frame_size = frame_size; + sd->mode = VAD_MODE_ADAPTIVE; + sd->min_signal_cnt = 10; + sd->min_silence_cnt = 64; + sd->recalc_cnt = 250; + sd->cur_threshold = 20; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_silence_det_set_fixed( pjmedia_silence_det *sd, + unsigned frame_size, + unsigned threshold ) +{ + PJ_ASSERT_RETURN(sd && frame_size, PJ_EINVAL); + + sd->mode = VAD_MODE_FIXED; + sd->frame_size = frame_size; + sd->cur_threshold = threshold; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pjmedia_silence_det_disable( pjmedia_silence_det *sd ) +{ + PJ_ASSERT_RETURN(sd, PJ_EINVAL); + + sd->mode = VAD_MODE_NONE; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_int32_t) pjmedia_silence_det_calc_avg_signal(const pj_int16_t samples[], + pj_size_t count) +{ + pj_uint32_t sum = 0; + + const pj_int16_t * pcm = samples; + const pj_int16_t * end = samples + count; + + if (count==0) + return 0; + + while (pcm != end) { + if (*pcm < 0) + sum -= *pcm++; + else + sum += *pcm++; + } + + return (pj_int32_t)(sum / count); +} + +PJ_DEF(pj_bool_t) pjmedia_silence_det_detect_silence( pjmedia_silence_det *sd, + const pj_int16_t samples[], + pj_size_t count, + pj_int32_t *p_level) +{ + pj_uint32_t level; + pj_bool_t have_signal; + + /* Always return false if VAD is disabled */ + if (sd->mode == VAD_MODE_NONE) { + if (p_level) + *p_level = -1; + return PJ_FALSE; + } + + /* Calculate average signal level. */ + level = pjmedia_silence_det_calc_avg_signal(samples, count); + + /* Report to caller, if required. */ + if (p_level) + *p_level = level; + + /* Convert PCM level to ulaw */ + level = linear2ulaw(level) ^ 0xff; + + /* Do we have signal? */ + have_signal = level > sd->cur_threshold; + + /* We we're in transition between silence and signel, increment the + * current frame counter. We will only switch mode when we have enough + * frames. + */ + if (sd->in_talk != have_signal) { + unsigned limit; + + sd->cur_cnt++; + + limit = (sd->in_talk ? sd->min_silence_cnt : + sd->min_signal_cnt); + + if (sd->cur_cnt > limit) { + + /* Swap mode */ + sd->in_talk = !sd->in_talk; + + /* Restart adaptive cur_threshold measurements */ + sd->weakest_signal = 0xFFFFFFFFUL; + sd->loudest_silence = 0; + sd->signal_cnt = 0; + sd->silence_cnt = 0; + } + + } else { + /* Reset frame count */ + sd->cur_cnt = 0; + } + + /* For fixed threshold sd, everything is done. */ + if (sd->mode == VAD_MODE_FIXED) { + return !sd->in_talk; + } + + + /* Count the number of silent and signal frames and calculate min/max */ + if (have_signal) { + if (level < sd->weakest_signal) + sd->weakest_signal = level; + sd->signal_cnt++; + } + else { + if (level > sd->loudest_silence) + sd->loudest_silence = level; + sd->silence_cnt++; + } + + /* See if we have had enough frames to look at proportions of + * silence/signal frames. + */ + if ((sd->signal_cnt + sd->silence_cnt) > sd->recalc_cnt) { + + /* Adjust silence threshold by looking at the proportions of + * signal and silence frames. + */ + if (sd->signal_cnt >= sd->recalc_cnt) { + /* All frames where signal frames. + * Increase silence threshold. + */ + sd->cur_threshold += (sd->weakest_signal - sd->cur_threshold)/4; + PJ_LOG(6,(THIS_FILE, "Vad cur_threshold increased to %d", + sd->cur_threshold)); + } + else if (sd->silence_cnt >= sd->recalc_cnt) { + /* All frames where silence frames. + * Decrease silence threshold. + */ + sd->cur_threshold = (sd->cur_threshold+sd->loudest_silence)/2+1; + PJ_LOG(6,(THIS_FILE, "Vad cur_threshold decreased to %d", + sd->cur_threshold)); + } + else { + pj_bool_t updated = PJ_TRUE; + + /* Adjust according to signal/silence proportions. */ + if (sd->signal_cnt > sd->silence_cnt * 2) + sd->cur_threshold++; + else if (sd->silence_cnt > sd->signal_cnt* 2) + sd->cur_threshold--; + else + updated = PJ_FALSE; + + if (updated) { + PJ_LOG(6,(THIS_FILE, + "Vad cur_threshold updated to %d", + sd->cur_threshold)); + } + } + + /* Reset. */ + sd->weakest_signal = 0xFFFFFFFFUL; + sd->loudest_silence = 0; + sd->signal_cnt = 0; + sd->silence_cnt = 0; + } + + return !sd->in_talk; +} + diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 14f46d2b..cbeaa2b7 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -116,6 +116,12 @@ struct pjmedia_stream }; +/* RFC 2833 digit */ +static const char digitmap[16] = { '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', '*', '#', + 'A', 'B', 'C', 'D'}; + /* * Print error. */ @@ -230,8 +236,17 @@ static void create_dtmf_payload(pjmedia_stream *stream, stream->tx_dtmf_buf[0].start_ts = cur_ts; pj_mutex_unlock(stream->jb_mutex); + + if (stream->tx_dtmf_count) + PJ_LOG(5,(THIS_FILE,"Sending DTMF digit id %c", + digitmap[stream->tx_dtmf_buf[0].event])); + + } else if (duration == 0) { + PJ_LOG(5,(THIS_FILE,"Sending DTMF digit id %c", + digitmap[digit->event])); } + frame_out->size = 4; } @@ -367,10 +382,6 @@ static void dump_bin(const char *buf, unsigned len) static void handle_incoming_dtmf( pjmedia_stream *stream, const void *payload, unsigned payloadlen) { - static const char digitmap[16] = { '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', '*', '#', - 'A', 'B', 'C', 'D'}; const pjmedia_rtp_dtmf_event *event = payload; /* Check compiler packing. */ diff --git a/pjmedia/src/pjmedia/vad.c b/pjmedia/src/pjmedia/vad.c deleted file mode 100644 index 645f3fe6..00000000 --- a/pjmedia/src/pjmedia/vad.c +++ /dev/null @@ -1,269 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 -#include -#include -#include -#include - - -#define THIS_FILE "vad.c" - -typedef enum pjmedia_vad_mode { - VAD_MODE_NONE, - VAD_MODE_FIXED, - VAD_MODE_ADAPTIVE -} pjmedia_vad_mode; - - -/** - * This structure holds the vad state. - */ -struct pjmedia_vad -{ - int mode; /**< VAD mode. */ - unsigned frame_size; /**< Samples per frame. */ - - - unsigned min_signal_cnt; /**< # of signal frames.before talk burst */ - unsigned min_silence_cnt; /**< # of silence frames before silence. */ - unsigned recalc_cnt; /**< # of frames before adaptive recalc. */ - - pj_bool_t in_talk; /**< In talk burst? */ - unsigned cur_cnt; /**< # of frames in current mode. */ - unsigned signal_cnt; /**< # of signal frames received. */ - unsigned silence_cnt; /**< # of silence frames received */ - unsigned cur_threshold; /**< Current silence threshold. */ - unsigned weakest_signal; /**< Weakest signal detected. */ - unsigned loudest_silence; /**< Loudest silence detected. */ -}; - - - -unsigned char linear2ulaw(int pcm_val); - - -PJ_DEF(pj_status_t) pjmedia_vad_create( pj_pool_t *pool, - pjmedia_vad **p_vad) -{ - pjmedia_vad *vad; - - PJ_ASSERT_RETURN(pool && p_vad, PJ_EINVAL); - - vad = pj_pool_zalloc(pool, sizeof(struct pjmedia_vad)); - - vad->weakest_signal = 0xFFFFFFFFUL; - vad->loudest_silence = 0; - vad->signal_cnt = 0; - vad->silence_cnt = 0; - - /* Restart in adaptive, silent mode */ - vad->in_talk = PJ_FALSE; - pjmedia_vad_set_adaptive( vad, 160 ); - - *p_vad = vad; - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pjmedia_vad_set_adaptive( pjmedia_vad *vad, - unsigned frame_size) -{ - PJ_ASSERT_RETURN(vad && frame_size, PJ_EINVAL); - - vad->frame_size = frame_size; - vad->mode = VAD_MODE_ADAPTIVE; - vad->min_signal_cnt = 10; - vad->min_silence_cnt = 64; - vad->recalc_cnt = 250; - vad->cur_threshold = 20; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pjmedia_vad_set_fixed( pjmedia_vad *vad, - unsigned frame_size, - unsigned threshold ) -{ - PJ_ASSERT_RETURN(vad && frame_size, PJ_EINVAL); - - vad->mode = VAD_MODE_FIXED; - vad->frame_size = frame_size; - vad->cur_threshold = threshold; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pjmedia_vad_disable( pjmedia_vad *vad ) -{ - PJ_ASSERT_RETURN(vad, PJ_EINVAL); - - vad->mode = VAD_MODE_NONE; - - return PJ_SUCCESS; -} - - -PJ_DEF(pj_int32_t) pjmedia_vad_calc_avg_signal(const pj_int16_t samples[], - pj_size_t count) -{ - pj_uint32_t sum = 0; - - const pj_int16_t * pcm = samples; - const pj_int16_t * end = samples + count; - - if (count==0) - return 0; - - while (pcm != end) { - if (*pcm < 0) - sum -= *pcm++; - else - sum += *pcm++; - } - - return (pj_int32_t)(sum / count); -} - -PJ_DEF(pj_bool_t) pjmedia_vad_detect_silence( pjmedia_vad *vad, - const pj_int16_t samples[], - pj_size_t count, - pj_int32_t *p_level) -{ - pj_uint32_t level; - pj_bool_t have_signal; - - /* Always return false if VAD is disabled */ - if (vad->mode == VAD_MODE_NONE) { - if (p_level) - *p_level = -1; - return PJ_FALSE; - } - - /* Calculate average signal level. */ - level = pjmedia_vad_calc_avg_signal(samples, count); - - /* Report to caller, if required. */ - if (p_level) - *p_level = level; - - /* Convert PCM level to ulaw */ - level = linear2ulaw(level) ^ 0xff; - - /* Do we have signal? */ - have_signal = level > vad->cur_threshold; - - /* We we're in transition between silence and signel, increment the - * current frame counter. We will only switch mode when we have enough - * frames. - */ - if (vad->in_talk != have_signal) { - unsigned limit; - - vad->cur_cnt++; - - limit = (vad->in_talk ? vad->min_silence_cnt : - vad->min_signal_cnt); - - if (vad->cur_cnt > limit) { - - /* Swap mode */ - vad->in_talk = !vad->in_talk; - - /* Restart adaptive cur_threshold measurements */ - vad->weakest_signal = 0xFFFFFFFFUL; - vad->loudest_silence = 0; - vad->signal_cnt = 0; - vad->silence_cnt = 0; - } - - } else { - /* Reset frame count */ - vad->cur_cnt = 0; - } - - /* For fixed threshold vad, everything is done. */ - if (vad->mode == VAD_MODE_FIXED) { - return !vad->in_talk; - } - - - /* Count the number of silent and signal frames and calculate min/max */ - if (have_signal) { - if (level < vad->weakest_signal) - vad->weakest_signal = level; - vad->signal_cnt++; - } - else { - if (level > vad->loudest_silence) - vad->loudest_silence = level; - vad->silence_cnt++; - } - - /* See if we have had enough frames to look at proportions of - * silence/signal frames. - */ - if ((vad->signal_cnt + vad->silence_cnt) > vad->recalc_cnt) { - - /* Adjust silence threshold by looking at the proportions of - * signal and silence frames. - */ - if (vad->signal_cnt >= vad->recalc_cnt) { - /* All frames where signal frames. - * Increase silence threshold. - */ - vad->cur_threshold += (vad->weakest_signal - vad->cur_threshold)/4; - PJ_LOG(6,(THIS_FILE, "Vad cur_threshold increased to %d", - vad->cur_threshold)); - } - else if (vad->silence_cnt >= vad->recalc_cnt) { - /* All frames where silence frames. - * Decrease silence threshold. - */ - vad->cur_threshold = (vad->cur_threshold+vad->loudest_silence)/2+1; - PJ_LOG(6,(THIS_FILE, "Vad cur_threshold decreased to %d", - vad->cur_threshold)); - } - else { - pj_bool_t updated = PJ_TRUE; - - /* Adjust according to signal/silence proportions. */ - if (vad->signal_cnt > vad->silence_cnt * 2) - vad->cur_threshold++; - else if (vad->silence_cnt > vad->signal_cnt* 2) - vad->cur_threshold--; - else - updated = PJ_FALSE; - - if (updated) { - PJ_LOG(6,(THIS_FILE, - "Vad cur_threshold updated to %d", - vad->cur_threshold)); - } - } - - /* Reset. */ - vad->weakest_signal = 0xFFFFFFFFUL; - vad->loudest_silence = 0; - vad->signal_cnt = 0; - vad->silence_cnt = 0; - } - - return !vad->in_talk; -} - -- cgit v1.2.3