| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include <math.h> |
| #include <memory.h> |
| #include <stdlib.h> |
| |
| #include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h" |
| #include "modules/audio_coding/codecs/isac/main/source/os_specific_inline.h" |
| #include "rtc_base/compile_assert_c.h" |
| |
| /* |
| * We are implementing the following filters; |
| * |
| * Pre-filtering: |
| * y(z) = x(z) + damper(z) * gain * (x(z) + y(z)) * z ^ (-lag); |
| * |
| * Post-filtering: |
| * y(z) = x(z) - damper(z) * gain * (x(z) + y(z)) * z ^ (-lag); |
| * |
| * Note that `lag` is a floating number so we perform an interpolation to |
| * obtain the correct `lag`. |
| * |
| */ |
| |
| static const double kDampFilter[PITCH_DAMPORDER] = {-0.07, 0.25, 0.64, 0.25, |
| -0.07}; |
| |
| /* interpolation coefficients; generated by design_pitch_filter.m */ |
| static const double kIntrpCoef[PITCH_FRACS][PITCH_FRACORDER] = { |
| {-0.02239172458614, 0.06653315052934, -0.16515880017569, 0.60701333734125, |
| 0.64671399919202, -0.20249000396417, 0.09926548334755, -0.04765933793109, |
| 0.01754159521746}, |
| {-0.01985640750434, 0.05816126837866, -0.13991265473714, 0.44560418147643, |
| 0.79117042386876, -0.20266133815188, 0.09585268418555, -0.04533310458084, |
| 0.01654127246314}, |
| {-0.01463300534216, 0.04229888475060, -0.09897034715253, 0.28284326017787, |
| 0.90385267956632, -0.16976950138649, 0.07704272393639, -0.03584218578311, |
| 0.01295781500709}, |
| {-0.00764851320885, 0.02184035544377, -0.04985561057281, 0.13083306574393, |
| 0.97545011664662, -0.10177807997561, 0.04400901776474, -0.02010737175166, |
| 0.00719783432422}, |
| {-0.00000000000000, 0.00000000000000, -0.00000000000001, 0.00000000000001, |
| 0.99999999999999, 0.00000000000001, -0.00000000000001, 0.00000000000000, |
| -0.00000000000000}, |
| {0.00719783432422, -0.02010737175166, 0.04400901776474, -0.10177807997562, |
| 0.97545011664663, 0.13083306574393, -0.04985561057280, 0.02184035544377, |
| -0.00764851320885}, |
| {0.01295781500710, -0.03584218578312, 0.07704272393640, -0.16976950138650, |
| 0.90385267956634, 0.28284326017785, -0.09897034715252, 0.04229888475059, |
| -0.01463300534216}, |
| {0.01654127246315, -0.04533310458085, 0.09585268418557, -0.20266133815190, |
| 0.79117042386878, 0.44560418147640, -0.13991265473712, 0.05816126837865, |
| -0.01985640750433} |
| }; |
| |
| /* |
| * Enumerating the operation of the filter. |
| * iSAC has 4 different pitch-filter which are very similar in their structure. |
| * |
| * kPitchFilterPre : In this mode the filter is operating as pitch |
| * pre-filter. This is used at the encoder. |
| * kPitchFilterPost : In this mode the filter is operating as pitch |
| * post-filter. This is the inverse of pre-filter and used |
| * in the decoder. |
| * kPitchFilterPreLa : This is, in structure, similar to pre-filtering but |
| * utilizing 3 millisecond lookahead. It is used to |
| * obtain the signal for LPC analysis. |
| * kPitchFilterPreGain : This is, in structure, similar to pre-filtering but |
| * differential changes in gain is considered. This is |
| * used to find the optimal gain. |
| */ |
| typedef enum { |
| kPitchFilterPre, kPitchFilterPost, kPitchFilterPreLa, kPitchFilterPreGain |
| } PitchFilterOperation; |
| |
| /* |
| * Structure with parameters used for pitch-filtering. |
| * buffer : a buffer where the sum of previous inputs and outputs |
| * are stored. |
| * damper_state : the state of the damping filter. The filter is defined by |
| * `kDampFilter`. |
| * interpol_coeff : pointer to a set of coefficient which are used to utilize |
| * fractional pitch by interpolation. |
| * gain : pitch-gain to be applied to the current segment of input. |
| * lag : pitch-lag for the current segment of input. |
| * lag_offset : the offset of lag w.r.t. current sample. |
| * sub_frame : sub-frame index, there are 4 pitch sub-frames in an iSAC |
| * frame. |
| * This specifies the usage of the filter. See |
| * 'PitchFilterOperation' for operational modes. |
| * num_samples : number of samples to be processed in each segment. |
| * index : index of the input and output sample. |
| * damper_state_dg : state of damping filter for different trial gains. |
| * gain_mult : differential changes to gain. |
| */ |
| typedef struct { |
| double buffer[PITCH_INTBUFFSIZE + QLOOKAHEAD]; |
| double damper_state[PITCH_DAMPORDER]; |
| const double *interpol_coeff; |
| double gain; |
| double lag; |
| int lag_offset; |
| |
| int sub_frame; |
| PitchFilterOperation mode; |
| int num_samples; |
| int index; |
| |
| double damper_state_dg[4][PITCH_DAMPORDER]; |
| double gain_mult[4]; |
| } PitchFilterParam; |
| |
| /********************************************************************** |
| * FilterSegment() |
| * Filter one segment, a quarter of a frame. |
| * |
| * Inputs |
| * in_data : pointer to the input signal of 30 ms at 8 kHz sample-rate. |
| * filter_param : pitch filter parameters. |
| * |
| * Outputs |
| * out_data : pointer to a buffer where the filtered signal is written to. |
| * out_dg : [only used in kPitchFilterPreGain] pointer to a buffer |
| * where the output of different gain values (differential |
| * change to gain) is written. |
| */ |
| static void FilterSegment(const double* in_data, PitchFilterParam* parameters, |
| double* out_data, |
| double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD]) { |
| int n; |
| int m; |
| int j; |
| double sum; |
| double sum2; |
| /* Index of `parameters->buffer` where the output is written to. */ |
| int pos = parameters->index + PITCH_BUFFSIZE; |
| /* Index of `parameters->buffer` where samples are read for fractional-lag |
| * computation. */ |
| int pos_lag = pos - parameters->lag_offset; |
| |
| for (n = 0; n < parameters->num_samples; ++n) { |
| /* Shift low pass filter states. */ |
| for (m = PITCH_DAMPORDER - 1; m > 0; --m) { |
| parameters->damper_state[m] = parameters->damper_state[m - 1]; |
| } |
| /* Filter to get fractional pitch. */ |
| sum = 0.0; |
| for (m = 0; m < PITCH_FRACORDER; ++m) { |
| sum += parameters->buffer[pos_lag + m] * parameters->interpol_coeff[m]; |
| } |
| /* Multiply with gain. */ |
| parameters->damper_state[0] = parameters->gain * sum; |
| |
| if (parameters->mode == kPitchFilterPreGain) { |
| int lag_index = parameters->index - parameters->lag_offset; |
| int m_tmp = (lag_index < 0) ? -lag_index : 0; |
| /* Update the damper state for the new sample. */ |
| for (m = PITCH_DAMPORDER - 1; m > 0; --m) { |
| for (j = 0; j < 4; ++j) { |
| parameters->damper_state_dg[j][m] = |
| parameters->damper_state_dg[j][m - 1]; |
| } |
| } |
| |
| for (j = 0; j < parameters->sub_frame + 1; ++j) { |
| /* Filter for fractional pitch. */ |
| sum2 = 0.0; |
| for (m = PITCH_FRACORDER-1; m >= m_tmp; --m) { |
| /* `lag_index + m` is always larger than or equal to zero, see how |
| * m_tmp is computed. This is equivalent to assume samples outside |
| * `out_dg[j]` are zero. */ |
| sum2 += out_dg[j][lag_index + m] * parameters->interpol_coeff[m]; |
| } |
| /* Add the contribution of differential gain change. */ |
| parameters->damper_state_dg[j][0] = parameters->gain_mult[j] * sum + |
| parameters->gain * sum2; |
| } |
| |
| /* Filter with damping filter, and store the results. */ |
| for (j = 0; j < parameters->sub_frame + 1; ++j) { |
| sum = 0.0; |
| for (m = 0; m < PITCH_DAMPORDER; ++m) { |
| sum -= parameters->damper_state_dg[j][m] * kDampFilter[m]; |
| } |
| out_dg[j][parameters->index] = sum; |
| } |
| } |
| /* Filter with damping filter. */ |
| sum = 0.0; |
| for (m = 0; m < PITCH_DAMPORDER; ++m) { |
| sum += parameters->damper_state[m] * kDampFilter[m]; |
| } |
| |
| /* Subtract from input and update buffer. */ |
| out_data[parameters->index] = in_data[parameters->index] - sum; |
| parameters->buffer[pos] = in_data[parameters->index] + |
| out_data[parameters->index]; |
| |
| ++parameters->index; |
| ++pos; |
| ++pos_lag; |
| } |
| return; |
| } |
| |
| /* Update filter parameters based on the pitch-gains and pitch-lags. */ |
| static void Update(PitchFilterParam* parameters) { |
| double fraction; |
| int fraction_index; |
| /* Compute integer lag-offset. */ |
| parameters->lag_offset = WebRtcIsac_lrint(parameters->lag + PITCH_FILTDELAY + |
| 0.5); |
| /* Find correct set of coefficients for computing fractional pitch. */ |
| fraction = parameters->lag_offset - (parameters->lag + PITCH_FILTDELAY); |
| fraction_index = WebRtcIsac_lrint(PITCH_FRACS * fraction - 0.5); |
| parameters->interpol_coeff = kIntrpCoef[fraction_index]; |
| |
| if (parameters->mode == kPitchFilterPreGain) { |
| /* If in this mode make a differential change to pitch gain. */ |
| parameters->gain_mult[parameters->sub_frame] += 0.2; |
| if (parameters->gain_mult[parameters->sub_frame] > 1.0) { |
| parameters->gain_mult[parameters->sub_frame] = 1.0; |
| } |
| if (parameters->sub_frame > 0) { |
| parameters->gain_mult[parameters->sub_frame - 1] -= 0.2; |
| } |
| } |
| } |
| |
| /****************************************************************************** |
| * FilterFrame() |
| * Filter a frame of 30 millisecond, given pitch-lags and pitch-gains. |
| * |
| * Inputs |
| * in_data : pointer to the input signal of 30 ms at 8 kHz sample-rate. |
| * lags : pointer to pitch-lags, 4 lags per frame. |
| * gains : pointer to pitch-gians, 4 gains per frame. |
| * mode : defining the functionality of the filter. It takes the |
| * following values. |
| * kPitchFilterPre: Pitch pre-filter, used at encoder. |
| * kPitchFilterPost: Pitch post-filter, used at decoder. |
| * kPitchFilterPreLa: Pitch pre-filter with lookahead. |
| * kPitchFilterPreGain: Pitch pre-filter used to otain optimal |
| * pitch-gains. |
| * |
| * Outputs |
| * out_data : pointer to a buffer where the filtered signal is written to. |
| * out_dg : [only used in kPitchFilterPreGain] pointer to a buffer |
| * where the output of different gain values (differential |
| * change to gain) is written. |
| */ |
| static void FilterFrame(const double* in_data, PitchFiltstr* filter_state, |
| double* lags, double* gains, PitchFilterOperation mode, |
| double* out_data, |
| double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD]) { |
| PitchFilterParam filter_parameters; |
| double gain_delta, lag_delta; |
| double old_lag, old_gain; |
| int n; |
| int m; |
| const double kEnhancer = 1.3; |
| |
| /* Set up buffer and states. */ |
| filter_parameters.index = 0; |
| filter_parameters.lag_offset = 0; |
| filter_parameters.mode = mode; |
| /* Copy states to local variables. */ |
| memcpy(filter_parameters.buffer, filter_state->ubuf, |
| sizeof(filter_state->ubuf)); |
| RTC_COMPILE_ASSERT(sizeof(filter_parameters.buffer) >= |
| sizeof(filter_state->ubuf)); |
| memset(filter_parameters.buffer + |
| sizeof(filter_state->ubuf) / sizeof(filter_state->ubuf[0]), |
| 0, sizeof(filter_parameters.buffer) - sizeof(filter_state->ubuf)); |
| memcpy(filter_parameters.damper_state, filter_state->ystate, |
| sizeof(filter_state->ystate)); |
| |
| if (mode == kPitchFilterPreGain) { |
| /* Clear buffers. */ |
| memset(filter_parameters.gain_mult, 0, sizeof(filter_parameters.gain_mult)); |
| memset(filter_parameters.damper_state_dg, 0, |
| sizeof(filter_parameters.damper_state_dg)); |
| for (n = 0; n < PITCH_SUBFRAMES; ++n) { |
| //memset(out_dg[n], 0, sizeof(double) * (PITCH_FRAME_LEN + QLOOKAHEAD)); |
| memset(out_dg[n], 0, sizeof(out_dg[n])); |
| } |
| } else if (mode == kPitchFilterPost) { |
| /* Make output more periodic. Negative sign is to change the structure |
| * of the filter. */ |
| for (n = 0; n < PITCH_SUBFRAMES; ++n) { |
| gains[n] *= -kEnhancer; |
| } |
| } |
| |
| old_lag = *filter_state->oldlagp; |
| old_gain = *filter_state->oldgainp; |
| |
| /* No interpolation if pitch lag step is big. */ |
| if ((lags[0] > (PITCH_UPSTEP * old_lag)) || |
| (lags[0] < (PITCH_DOWNSTEP * old_lag))) { |
| old_lag = lags[0]; |
| old_gain = gains[0]; |
| |
| if (mode == kPitchFilterPreGain) { |
| filter_parameters.gain_mult[0] = 1.0; |
| } |
| } |
| |
| filter_parameters.num_samples = PITCH_UPDATE; |
| for (m = 0; m < PITCH_SUBFRAMES; ++m) { |
| /* Set the sub-frame value. */ |
| filter_parameters.sub_frame = m; |
| /* Calculate interpolation steps for pitch-lag and pitch-gain. */ |
| lag_delta = (lags[m] - old_lag) / PITCH_GRAN_PER_SUBFRAME; |
| filter_parameters.lag = old_lag; |
| gain_delta = (gains[m] - old_gain) / PITCH_GRAN_PER_SUBFRAME; |
| filter_parameters.gain = old_gain; |
| /* Store for the next sub-frame. */ |
| old_lag = lags[m]; |
| old_gain = gains[m]; |
| |
| for (n = 0; n < PITCH_GRAN_PER_SUBFRAME; ++n) { |
| /* Step-wise interpolation of pitch gains and lags. As pitch-lag changes, |
| * some parameters of filter need to be update. */ |
| filter_parameters.gain += gain_delta; |
| filter_parameters.lag += lag_delta; |
| /* Update parameters according to new lag value. */ |
| Update(&filter_parameters); |
| /* Filter a segment of input. */ |
| FilterSegment(in_data, &filter_parameters, out_data, out_dg); |
| } |
| } |
| |
| if (mode != kPitchFilterPreGain) { |
| /* Export buffer and states. */ |
| memcpy(filter_state->ubuf, &filter_parameters.buffer[PITCH_FRAME_LEN], |
| sizeof(filter_state->ubuf)); |
| memcpy(filter_state->ystate, filter_parameters.damper_state, |
| sizeof(filter_state->ystate)); |
| |
| /* Store for the next frame. */ |
| *filter_state->oldlagp = old_lag; |
| *filter_state->oldgainp = old_gain; |
| } |
| |
| if ((mode == kPitchFilterPreGain) || (mode == kPitchFilterPreLa)) { |
| /* Filter the lookahead segment, this is treated as the last sub-frame. So |
| * set `pf_param` to last sub-frame. */ |
| filter_parameters.sub_frame = PITCH_SUBFRAMES - 1; |
| filter_parameters.num_samples = QLOOKAHEAD; |
| FilterSegment(in_data, &filter_parameters, out_data, out_dg); |
| } |
| } |
| |
| void WebRtcIsac_PitchfilterPre(double* in_data, double* out_data, |
| PitchFiltstr* pf_state, double* lags, |
| double* gains) { |
| FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPre, out_data, NULL); |
| } |
| |
| void WebRtcIsac_PitchfilterPre_la(double* in_data, double* out_data, |
| PitchFiltstr* pf_state, double* lags, |
| double* gains) { |
| FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPreLa, out_data, |
| NULL); |
| } |
| |
| void WebRtcIsac_PitchfilterPre_gains( |
| double* in_data, double* out_data, |
| double out_dg[][PITCH_FRAME_LEN + QLOOKAHEAD], PitchFiltstr *pf_state, |
| double* lags, double* gains) { |
| FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPreGain, out_data, |
| out_dg); |
| } |
| |
| void WebRtcIsac_PitchfilterPost(double* in_data, double* out_data, |
| PitchFiltstr* pf_state, double* lags, |
| double* gains) { |
| FilterFrame(in_data, pf_state, lags, gains, kPitchFilterPost, out_data, NULL); |
| } |