Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/plc.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
| author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
|---|---|
| date | Fri, 25 Jun 2010 15:50:58 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
|---|---|
| 1 /* | |
| 2 * SpanDSP - a series of DSP components for telephony | |
| 3 * | |
| 4 * plc.c | |
| 5 * | |
| 6 * Written by Steve Underwood <steveu@coppice.org> | |
| 7 * | |
| 8 * Copyright (C) 2004 Steve Underwood | |
| 9 * | |
| 10 * All rights reserved. | |
| 11 * | |
| 12 * This program is free software; you can redistribute it and/or modify | |
| 13 * it under the terms of the GNU Lesser General Public License version 2.1, | |
| 14 * as published by the Free Software Foundation. | |
| 15 * | |
| 16 * This program is distributed in the hope that it will be useful, | |
| 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 19 * GNU Lesser General Public License for more details. | |
| 20 * | |
| 21 * You should have received a copy of the GNU Lesser General Public | |
| 22 * License along with this program; if not, write to the Free Software | |
| 23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 24 * | |
| 25 * $Id: plc.c,v 1.27.4.1 2009/12/23 14:23:49 steveu Exp $ | |
| 26 */ | |
| 27 | |
| 28 /*! \file */ | |
| 29 | |
| 30 #if defined(HAVE_CONFIG_H) | |
| 31 #include "config.h" | |
| 32 #endif | |
| 33 | |
| 34 #include <stdio.h> | |
| 35 #include <inttypes.h> | |
| 36 #include <stdlib.h> | |
| 37 #include <string.h> | |
| 38 #if defined(HAVE_TGMATH_H) | |
| 39 #include <tgmath.h> | |
| 40 #endif | |
| 41 #if defined(HAVE_MATH_H) | |
| 42 #include <math.h> | |
| 43 #endif | |
| 44 #include "floating_fudge.h" | |
| 45 #include <limits.h> | |
| 46 | |
| 47 #include "spandsp/telephony.h" | |
| 48 #include "spandsp/fast_convert.h" | |
| 49 #include "spandsp/saturated.h" | |
| 50 #include "spandsp/plc.h" | |
| 51 | |
| 52 /* We do a straight line fade to zero volume in 50ms when we are filling in for missing data. */ | |
| 53 #define ATTENUATION_INCREMENT 0.0025f /* Attenuation per sample */ | |
| 54 | |
| 55 static void save_history(plc_state_t *s, int16_t *buf, int len) | |
| 56 { | |
| 57 if (len >= PLC_HISTORY_LEN) | |
| 58 { | |
| 59 /* Just keep the last part of the new data, starting at the beginning of the buffer */ | |
| 60 memcpy(s->history, buf + len - PLC_HISTORY_LEN, sizeof(int16_t)*PLC_HISTORY_LEN); | |
| 61 s->buf_ptr = 0; | |
| 62 return; | |
| 63 } | |
| 64 if (s->buf_ptr + len > PLC_HISTORY_LEN) | |
| 65 { | |
| 66 /* Wraps around - must break into two sections */ | |
| 67 memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*(PLC_HISTORY_LEN - s->buf_ptr)); | |
| 68 len -= (PLC_HISTORY_LEN - s->buf_ptr); | |
| 69 memcpy(s->history, buf + (PLC_HISTORY_LEN - s->buf_ptr), sizeof(int16_t)*len); | |
| 70 s->buf_ptr = len; | |
| 71 return; | |
| 72 } | |
| 73 /* Can use just one section */ | |
| 74 memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*len); | |
| 75 s->buf_ptr += len; | |
| 76 } | |
| 77 /*- End of function --------------------------------------------------------*/ | |
| 78 | |
| 79 static __inline__ void normalise_history(plc_state_t *s) | |
| 80 { | |
| 81 int16_t tmp[PLC_HISTORY_LEN]; | |
| 82 | |
| 83 if (s->buf_ptr == 0) | |
| 84 return; | |
| 85 memcpy(tmp, s->history, sizeof(int16_t)*s->buf_ptr); | |
| 86 memcpy(s->history, s->history + s->buf_ptr, sizeof(int16_t)*(PLC_HISTORY_LEN - s->buf_ptr)); | |
| 87 memcpy(s->history + PLC_HISTORY_LEN - s->buf_ptr, tmp, sizeof(int16_t)*s->buf_ptr); | |
| 88 s->buf_ptr = 0; | |
| 89 } | |
| 90 /*- End of function --------------------------------------------------------*/ | |
| 91 | |
| 92 static __inline__ int amdf_pitch(int min_pitch, int max_pitch, int16_t amp[], int len) | |
| 93 { | |
| 94 int i; | |
| 95 int j; | |
| 96 int acc; | |
| 97 int min_acc; | |
| 98 int pitch; | |
| 99 | |
| 100 pitch = min_pitch; | |
| 101 min_acc = INT_MAX; | |
| 102 for (i = max_pitch; i <= min_pitch; i++) | |
| 103 { | |
| 104 acc = 0; | |
| 105 for (j = 0; j < len; j++) | |
| 106 acc += abs(amp[i + j] - amp[j]); | |
| 107 if (acc < min_acc) | |
| 108 { | |
| 109 min_acc = acc; | |
| 110 pitch = i; | |
| 111 } | |
| 112 } | |
| 113 return pitch; | |
| 114 } | |
| 115 /*- End of function --------------------------------------------------------*/ | |
| 116 | |
| 117 SPAN_DECLARE(int) plc_rx(plc_state_t *s, int16_t amp[], int len) | |
| 118 { | |
| 119 int i; | |
| 120 int pitch_overlap; | |
| 121 float old_step; | |
| 122 float new_step; | |
| 123 float old_weight; | |
| 124 float new_weight; | |
| 125 float gain; | |
| 126 | |
| 127 if (s->missing_samples) | |
| 128 { | |
| 129 /* Although we have a real signal, we need to smooth it to fit well | |
| 130 with the synthetic signal we used for the previous block */ | |
| 131 | |
| 132 /* The start of the real data is overlapped with the next 1/4 cycle | |
| 133 of the synthetic data. */ | |
| 134 pitch_overlap = s->pitch >> 2; | |
| 135 if (pitch_overlap > len) | |
| 136 pitch_overlap = len; | |
| 137 gain = 1.0f - s->missing_samples*ATTENUATION_INCREMENT; | |
| 138 if (gain < 0.0f) | |
| 139 gain = 0.0f; | |
| 140 new_step = 1.0f/pitch_overlap; | |
| 141 old_step = new_step*gain; | |
| 142 new_weight = new_step; | |
| 143 old_weight = (1.0f - new_step)*gain; | |
| 144 for (i = 0; i < pitch_overlap; i++) | |
| 145 { | |
| 146 amp[i] = fsaturate(old_weight*s->pitchbuf[s->pitch_offset] + new_weight*amp[i]); | |
| 147 if (++s->pitch_offset >= s->pitch) | |
| 148 s->pitch_offset = 0; | |
| 149 new_weight += new_step; | |
| 150 old_weight -= old_step; | |
| 151 if (old_weight < 0.0f) | |
| 152 old_weight = 0.0f; | |
| 153 } | |
| 154 s->missing_samples = 0; | |
| 155 } | |
| 156 save_history(s, amp, len); | |
| 157 return len; | |
| 158 } | |
| 159 /*- End of function --------------------------------------------------------*/ | |
| 160 | |
| 161 SPAN_DECLARE(int) plc_fillin(plc_state_t *s, int16_t amp[], int len) | |
| 162 { | |
| 163 int i; | |
| 164 int pitch_overlap; | |
| 165 float old_step; | |
| 166 float new_step; | |
| 167 float old_weight; | |
| 168 float new_weight; | |
| 169 float gain; | |
| 170 int16_t *orig_amp; | |
| 171 int orig_len; | |
| 172 | |
| 173 orig_amp = amp; | |
| 174 orig_len = len; | |
| 175 if (s->missing_samples == 0) | |
| 176 { | |
| 177 /* As the gap in real speech starts we need to assess the last known pitch, | |
| 178 and prepare the synthetic data we will use for fill-in */ | |
| 179 normalise_history(s); | |
| 180 s->pitch = amdf_pitch(PLC_PITCH_MIN, PLC_PITCH_MAX, s->history + PLC_HISTORY_LEN - CORRELATION_SPAN - PLC_PITCH_MIN, CORRELATION_SPAN); | |
| 181 /* We overlap a 1/4 wavelength */ | |
| 182 pitch_overlap = s->pitch >> 2; | |
| 183 /* Cook up a single cycle of pitch, using a single of the real signal with 1/4 | |
| 184 cycle OLA'ed to make the ends join up nicely */ | |
| 185 /* The first 3/4 of the cycle is a simple copy */ | |
| 186 for (i = 0; i < s->pitch - pitch_overlap; i++) | |
| 187 s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i]; | |
| 188 /* The last 1/4 of the cycle is overlapped with the end of the previous cycle */ | |
| 189 new_step = 1.0f/pitch_overlap; | |
| 190 new_weight = new_step; | |
| 191 for ( ; i < s->pitch; i++) | |
| 192 { | |
| 193 s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i]*(1.0f - new_weight) + s->history[PLC_HISTORY_LEN - 2*s->pitch + i]*new_weight; | |
| 194 new_weight += new_step; | |
| 195 } | |
| 196 /* We should now be ready to fill in the gap with repeated, decaying cycles | |
| 197 of what is in pitchbuf */ | |
| 198 | |
| 199 gain = 1.0f; | |
| 200 /* We need to OLA the first 1/4 wavelength of the synthetic data, to smooth | |
| 201 it into the previous real data. To avoid the need to introduce a delay | |
| 202 in the stream, reverse the last 1/4 wavelength, and OLA with that. */ | |
| 203 new_step = 1.0f/pitch_overlap; | |
| 204 old_step = new_step; | |
| 205 new_weight = new_step; | |
| 206 old_weight = 1.0f - new_step; | |
| 207 for (i = 0; i < pitch_overlap; i++) | |
| 208 { | |
| 209 amp[i] = fsaturate(old_weight*s->history[PLC_HISTORY_LEN - 1 - i] + new_weight*s->pitchbuf[i]); | |
| 210 new_weight += new_step; | |
| 211 old_weight -= old_step; | |
| 212 if (old_weight < 0.0f) | |
| 213 old_weight = 0.0f; | |
| 214 } | |
| 215 s->pitch_offset = i; | |
| 216 } | |
| 217 else | |
| 218 { | |
| 219 gain = 1.0f - s->missing_samples*ATTENUATION_INCREMENT; | |
| 220 i = 0; | |
| 221 } | |
| 222 for ( ; gain > 0.0f && i < len; i++) | |
| 223 { | |
| 224 amp[i] = (int16_t) (s->pitchbuf[s->pitch_offset]*gain); | |
| 225 gain -= ATTENUATION_INCREMENT; | |
| 226 if (++s->pitch_offset >= s->pitch) | |
| 227 s->pitch_offset = 0; | |
| 228 } | |
| 229 for ( ; i < len; i++) | |
| 230 amp[i] = 0; | |
| 231 s->missing_samples += orig_len; | |
| 232 save_history(s, amp, len); | |
| 233 return len; | |
| 234 } | |
| 235 /*- End of function --------------------------------------------------------*/ | |
| 236 | |
| 237 SPAN_DECLARE(plc_state_t *) plc_init(plc_state_t *s) | |
| 238 { | |
| 239 if (s == NULL) | |
| 240 { | |
| 241 if ((s = (plc_state_t *) malloc(sizeof(*s))) == NULL) | |
| 242 return NULL; | |
| 243 } | |
| 244 memset(s, 0, sizeof(*s)); | |
| 245 return s; | |
| 246 } | |
| 247 /*- End of function --------------------------------------------------------*/ | |
| 248 | |
| 249 SPAN_DECLARE(int) plc_release(plc_state_t *s) | |
| 250 { | |
| 251 return 0; | |
| 252 } | |
| 253 /*- End of function --------------------------------------------------------*/ | |
| 254 | |
| 255 SPAN_DECLARE(int) plc_free(plc_state_t *s) | |
| 256 { | |
| 257 if (s) | |
| 258 free(s); | |
| 259 return 0; | |
| 260 } | |
| 261 /*- End of function --------------------------------------------------------*/ | |
| 262 /*- End of file ------------------------------------------------------------*/ |
