Added noise energy estimation and beginning of a VAD.
[speexdsp.git] / libspeex / vbr.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: vbr.c
3
4    VBR-related routines
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10    
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15    
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 */
21
22 #include "vbr.h"
23 #include <math.h>
24 #include <stdio.h>
25
26 #define sqr(x) ((x)*(x))
27
28 #define MIN_ENERGY 1000
29 #define NOISE_POW .3
30
31 void vbr_init(VBRState *vbr)
32 {
33    int i;
34
35    vbr->average_energy=0;
36    vbr->last_energy=1;
37    vbr->accum_sum=0;
38    vbr->energy_alpha=.1;
39    vbr->soft_pitch=0;
40    vbr->last_pitch_coef=0;
41    vbr->last_quality=0;
42
43    vbr->noise_accum = .05*pow(MIN_ENERGY, NOISE_POW);
44    vbr->noise_accum_count=.05;
45    vbr->noise_level=vbr->noise_accum/vbr->noise_accum_count;
46    vbr->consec_noise=0;
47
48
49    for (i=0;i<VBR_MEMORY_SIZE;i++)
50       vbr->last_log_energy[i] = log(MIN_ENERGY);
51 }
52
53
54 /*
55   This function should analyse the signal and decide how critical the
56   coding error will be perceptually. The following factors should be
57   taken into account:
58
59   -Attacks (positive energy derivative) should be coded with more bits
60
61   -Stationary voiced segments should receive more bits
62
63   -Segments with (very) low absolute energy should receive less bits (maybe
64   only shaped noise?)
65
66   -DTX for near-zero energy?
67
68   -Stationary fricative segments should have less bits
69
70   -Temporal masking: when energy slope is decreasing, decrease the bit-rate
71
72   -Decrease bit-rate for males (low pitch)?
73
74   -(wideband only) less bits in the high-band when signal is very 
75   non-stationary (harder to notice high-frequency noise)???
76
77 */
78 float vbr_analysis(VBRState *vbr, float *sig, int len, int pitch, float pitch_coef)
79 {
80    int i;
81    float ener=0, ener1=0, ener2=0;
82    float qual=0;
83    int va;
84    float log_energy;
85    float non_st=0;
86    float voicing;
87    float pow_ener;
88
89    for (i=0;i<len>>1;i++)
90       ener1 += sig[i]*sig[i];
91
92    for (i=len>>1;i<len;i++)
93       ener2 += sig[i]*sig[i];
94    ener=ener1+ener2;
95
96    log_energy = log(ener+MIN_ENERGY);
97    for (i=0;i<VBR_MEMORY_SIZE;i++)
98       non_st += sqr(log_energy-vbr->last_log_energy[i]);
99    non_st =  non_st/(30*VBR_MEMORY_SIZE);
100    if (non_st>1)
101       non_st=1;
102
103    voicing = 3*(pitch_coef-.4)*fabs(pitch_coef-.4);
104    vbr->average_energy = (1-vbr->energy_alpha)*vbr->average_energy + vbr->energy_alpha*ener;
105    vbr->noise_level=vbr->noise_accum/vbr->noise_accum_count;
106    pow_ener = pow(ener,NOISE_POW);
107    if ((voicing<.3 && non_st < .2 && pow_ener < 1.2*vbr->noise_level)
108        || (voicing<.2 && non_st < .1))
109    {
110       float tmp;
111       va = 0;
112       vbr->consec_noise++;
113       if (pow_ener > 3*vbr->noise_level)
114          tmp = 3*vbr->noise_level;
115       else 
116          tmp = pow_ener;
117       if (vbr->consec_noise>=4)
118       {
119          vbr->noise_accum = .95*vbr->noise_accum + .05*tmp;
120          vbr->noise_accum_count = .95*vbr->noise_accum_count + .05;
121       }
122    } else {
123       va = 1;
124       vbr->consec_noise=0;
125    }
126
127    /* Checking for "pseudo temporal masking" */
128    if (ener < .1*vbr->average_energy)
129       qual -= .7;
130    if (ener < .01*vbr->average_energy)
131       qual -= .7;
132    if (ener < .001*vbr->average_energy)
133       qual -= .7;
134    /* Checking for very low absolute energy */
135    if (ener < 30000)
136    {
137       qual -= .7;
138       if (ener < 10000)
139          qual-=.7;
140       if (ener < 3000)
141          qual-=.7;
142    } else {
143       /* Checking for energy increases */
144       if (ener > vbr->last_energy*4.0)
145          qual += 1;
146       if (ener > vbr->last_energy*1.8)
147          qual += 1;
148       if (ener > 3*vbr->average_energy)
149          qual += 1;
150       if (ener2 > 1.6*ener1)
151          qual += .7;
152       if (ener2 < .6*ener1)
153          qual -= .5;
154
155       if (ener < .3*vbr->last_energy)
156          qual -= .6;
157    }
158    vbr->soft_pitch = .6*vbr->soft_pitch + .4*pitch_coef;
159    qual += (pitch_coef-.4) + (vbr->soft_pitch-.4);
160
161    if (qual < vbr->last_quality)
162       qual = .5*qual + .5*vbr->last_quality;
163    if (qual<-3)
164       qual=-3;
165    if (qual>3)
166       qual=3;
167
168    vbr->last_pitch_coef = pitch_coef;
169    vbr->last_quality = qual;
170
171    for (i=VBR_MEMORY_SIZE-1;i>0;i--)
172       vbr->last_log_energy[i] = vbr->last_log_energy[i-1];
173    vbr->last_log_energy[0] = log_energy;
174
175    printf ("VBR: %f %f %f %d %f\n", (float)(log_energy-log(vbr->average_energy+MIN_ENERGY)), non_st, voicing, va, vbr->noise_level);
176
177    return qual;
178 }
179
180 void vbr_destroy(VBRState *vbr)
181 {
182 }