Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/tone_detect.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 * tone_detect.c - General telephony tone detection. | |
| 5 * | |
| 6 * Written by Steve Underwood <steveu@coppice.org> | |
| 7 * | |
| 8 * Copyright (C) 2001-2003, 2005 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: tone_detect.c,v 1.53 2009/04/12 09:12:10 steveu Exp $ | |
| 26 */ | |
| 27 | |
| 28 /*! \file */ | |
| 29 | |
| 30 #if defined(HAVE_CONFIG_H) | |
| 31 #include "config.h" | |
| 32 #endif | |
| 33 | |
| 34 #include <inttypes.h> | |
| 35 #include <stdlib.h> | |
| 36 #if defined(HAVE_TGMATH_H) | |
| 37 #include <tgmath.h> | |
| 38 #endif | |
| 39 #if defined(HAVE_MATH_H) | |
| 40 #include <math.h> | |
| 41 #endif | |
| 42 #include "floating_fudge.h" | |
| 43 #include <string.h> | |
| 44 #include <stdio.h> | |
| 45 #include <time.h> | |
| 46 #include <fcntl.h> | |
| 47 | |
| 48 #include "spandsp/telephony.h" | |
| 49 #include "spandsp/complex.h" | |
| 50 #include "spandsp/complex_vector_float.h" | |
| 51 #include "spandsp/tone_detect.h" | |
| 52 #include "spandsp/tone_generate.h" | |
| 53 | |
| 54 #include "spandsp/private/tone_detect.h" | |
| 55 | |
| 56 #if !defined(M_PI) | |
| 57 /* C99 systems may not define M_PI */ | |
| 58 #define M_PI 3.14159265358979323846264338327 | |
| 59 #endif | |
| 60 | |
| 61 SPAN_DECLARE(void) make_goertzel_descriptor(goertzel_descriptor_t *t, float freq, int samples) | |
| 62 { | |
| 63 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 64 t->fac = 16383.0f*2.0f*cosf(2.0f*M_PI*(freq/(float) SAMPLE_RATE)); | |
| 65 #else | |
| 66 t->fac = 2.0f*cosf(2.0f*M_PI*(freq/(float) SAMPLE_RATE)); | |
| 67 #endif | |
| 68 t->samples = samples; | |
| 69 } | |
| 70 /*- End of function --------------------------------------------------------*/ | |
| 71 | |
| 72 SPAN_DECLARE(goertzel_state_t *) goertzel_init(goertzel_state_t *s, | |
| 73 goertzel_descriptor_t *t) | |
| 74 { | |
| 75 if (s == NULL) | |
| 76 { | |
| 77 if ((s = (goertzel_state_t *) malloc(sizeof(*s))) == NULL) | |
| 78 return NULL; | |
| 79 } | |
| 80 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 81 s->v2 = | |
| 82 s->v3 = 0; | |
| 83 #else | |
| 84 s->v2 = | |
| 85 s->v3 = 0.0f; | |
| 86 #endif | |
| 87 s->fac = t->fac; | |
| 88 s->samples = t->samples; | |
| 89 s->current_sample = 0; | |
| 90 return s; | |
| 91 } | |
| 92 /*- End of function --------------------------------------------------------*/ | |
| 93 | |
| 94 SPAN_DECLARE(int) goertzel_release(goertzel_state_t *s) | |
| 95 { | |
| 96 return 0; | |
| 97 } | |
| 98 /*- End of function --------------------------------------------------------*/ | |
| 99 | |
| 100 SPAN_DECLARE(int) goertzel_free(goertzel_state_t *s) | |
| 101 { | |
| 102 if (s) | |
| 103 free(s); | |
| 104 return 0; | |
| 105 } | |
| 106 /*- End of function --------------------------------------------------------*/ | |
| 107 | |
| 108 SPAN_DECLARE(void) goertzel_reset(goertzel_state_t *s) | |
| 109 { | |
| 110 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 111 s->v2 = | |
| 112 s->v3 = 0; | |
| 113 #else | |
| 114 s->v2 = | |
| 115 s->v3 = 0.0f; | |
| 116 #endif | |
| 117 s->current_sample = 0; | |
| 118 } | |
| 119 /*- End of function --------------------------------------------------------*/ | |
| 120 | |
| 121 SPAN_DECLARE(int) goertzel_update(goertzel_state_t *s, | |
| 122 const int16_t amp[], | |
| 123 int samples) | |
| 124 { | |
| 125 int i; | |
| 126 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 127 int16_t x; | |
| 128 int16_t v1; | |
| 129 #else | |
| 130 float v1; | |
| 131 #endif | |
| 132 | |
| 133 if (samples > s->samples - s->current_sample) | |
| 134 samples = s->samples - s->current_sample; | |
| 135 for (i = 0; i < samples; i++) | |
| 136 { | |
| 137 v1 = s->v2; | |
| 138 s->v2 = s->v3; | |
| 139 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 140 x = (((int32_t) s->fac*s->v2) >> 14); | |
| 141 /* Scale down the input signal to avoid overflows. 9 bits is enough to | |
| 142 monitor the signals of interest with adequate dynamic range and | |
| 143 resolution. In telephony we generally only start with 13 or 14 bits, | |
| 144 anyway. */ | |
| 145 s->v3 = x - v1 + (amp[i] >> 7); | |
| 146 #else | |
| 147 s->v3 = s->fac*s->v2 - v1 + amp[i]; | |
| 148 #endif | |
| 149 } | |
| 150 s->current_sample += samples; | |
| 151 return samples; | |
| 152 } | |
| 153 /*- End of function --------------------------------------------------------*/ | |
| 154 | |
| 155 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 156 SPAN_DECLARE(int32_t) goertzel_result(goertzel_state_t *s) | |
| 157 #else | |
| 158 SPAN_DECLARE(float) goertzel_result(goertzel_state_t *s) | |
| 159 #endif | |
| 160 { | |
| 161 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 162 int16_t v1; | |
| 163 int32_t x; | |
| 164 int32_t y; | |
| 165 #else | |
| 166 float v1; | |
| 167 #endif | |
| 168 | |
| 169 /* Push a zero through the process to finish things off. */ | |
| 170 v1 = s->v2; | |
| 171 s->v2 = s->v3; | |
| 172 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 173 x = (((int32_t) s->fac*s->v2) >> 14); | |
| 174 s->v3 = x - v1; | |
| 175 #else | |
| 176 s->v3 = s->fac*s->v2 - v1; | |
| 177 #endif | |
| 178 /* Now calculate the non-recursive side of the filter. */ | |
| 179 /* The result here is not scaled down to allow for the magnification | |
| 180 effect of the filter (the usual DFT magnification effect). */ | |
| 181 #if defined(SPANDSP_USE_FIXED_POINT) | |
| 182 x = (int32_t) s->v3*s->v3; | |
| 183 y = (int32_t) s->v2*s->v2; | |
| 184 x += y; | |
| 185 y = ((int32_t) s->v3*s->fac) >> 14; | |
| 186 y *= s->v2; | |
| 187 x -= y; | |
| 188 x <<= 1; | |
| 189 goertzel_reset(s); | |
| 190 /* The number returned in a floating point build will be 16384^2 times | |
| 191 as big as for a fixed point build, due to the 14 bit shifts | |
| 192 (or the square of the 7 bit shifts, depending how you look at it). */ | |
| 193 return x; | |
| 194 #else | |
| 195 v1 = s->v3*s->v3 + s->v2*s->v2 - s->v2*s->v3*s->fac; | |
| 196 v1 *= 2.0; | |
| 197 goertzel_reset(s); | |
| 198 return v1; | |
| 199 #endif | |
| 200 } | |
| 201 /*- End of function --------------------------------------------------------*/ | |
| 202 | |
| 203 SPAN_DECLARE(complexf_t) periodogram(const complexf_t coeffs[], const complexf_t amp[], int len) | |
| 204 { | |
| 205 complexf_t sum; | |
| 206 complexf_t diff; | |
| 207 complexf_t x; | |
| 208 int i; | |
| 209 | |
| 210 x = complex_setf(0.0f, 0.0f); | |
| 211 for (i = 0; i < len/2; i++) | |
| 212 { | |
| 213 sum = complex_addf(&[i], &[len - 1 - i]); | |
| 214 diff = complex_subf(&[i], &[len - 1 - i]); | |
| 215 x.re += (coeffs[i].re*sum.re - coeffs[i].im*diff.im); | |
| 216 x.im += (coeffs[i].re*sum.im + coeffs[i].im*diff.re); | |
| 217 } | |
| 218 return x; | |
| 219 } | |
| 220 /*- End of function --------------------------------------------------------*/ | |
| 221 | |
| 222 SPAN_DECLARE(int) periodogram_prepare(complexf_t sum[], complexf_t diff[], const complexf_t amp[], int len) | |
| 223 { | |
| 224 int i; | |
| 225 | |
| 226 for (i = 0; i < len/2; i++) | |
| 227 { | |
| 228 sum[i] = complex_addf(&[i], &[len - 1 - i]); | |
| 229 diff[i] = complex_subf(&[i], &[len - 1 - i]); | |
| 230 } | |
| 231 return len/2; | |
| 232 } | |
| 233 /*- End of function --------------------------------------------------------*/ | |
| 234 | |
| 235 SPAN_DECLARE(complexf_t) periodogram_apply(const complexf_t coeffs[], const complexf_t sum[], const complexf_t diff[], int len) | |
| 236 { | |
| 237 complexf_t x; | |
| 238 int i; | |
| 239 | |
| 240 x = complex_setf(0.0f, 0.0f); | |
| 241 for (i = 0; i < len/2; i++) | |
| 242 { | |
| 243 x.re += (coeffs[i].re*sum[i].re - coeffs[i].im*diff[i].im); | |
| 244 x.im += (coeffs[i].re*sum[i].im + coeffs[i].im*diff[i].re); | |
| 245 } | |
| 246 return x; | |
| 247 } | |
| 248 /*- End of function --------------------------------------------------------*/ | |
| 249 | |
| 250 SPAN_DECLARE(int) periodogram_generate_coeffs(complexf_t coeffs[], float freq, int sample_rate, int window_len) | |
| 251 { | |
| 252 float window; | |
| 253 float sum; | |
| 254 float x; | |
| 255 int i; | |
| 256 | |
| 257 sum = 0.0f; | |
| 258 for (i = 0; i < window_len/2; i++) | |
| 259 { | |
| 260 /* Apply a Hamming window as we go */ | |
| 261 window = 0.53836f - 0.46164f*cosf(2.0f*3.1415926535f*i/(window_len - 1.0f)); | |
| 262 x = (i - window_len/2.0f + 0.5f)*freq*2.0f*3.1415926535f/sample_rate; | |
| 263 coeffs[i].re = cosf(x)*window; | |
| 264 coeffs[i].im = -sinf(x)*window; | |
| 265 sum += window; | |
| 266 } | |
| 267 /* Rescale for unity gain in the periodogram. The 2.0 factor is to allow for the full window, | |
| 268 rather than just the half over which we have summed the coefficients. */ | |
| 269 sum = 1.0f/(2.0f*sum); | |
| 270 for (i = 0; i < window_len/2; i++) | |
| 271 { | |
| 272 coeffs[i].re *= sum; | |
| 273 coeffs[i].im *= sum; | |
| 274 } | |
| 275 return window_len/2; | |
| 276 } | |
| 277 /*- End of function --------------------------------------------------------*/ | |
| 278 | |
| 279 SPAN_DECLARE(float) periodogram_generate_phase_offset(complexf_t *offset, float freq, int sample_rate, int interval) | |
| 280 { | |
| 281 float x; | |
| 282 | |
| 283 /* The phase offset is how far the phase rotates in one frame */ | |
| 284 x = 2.0f*3.1415926535f*(float) interval/(float) sample_rate; | |
| 285 offset->re = cosf(freq*x); | |
| 286 offset->im = sinf(freq*x); | |
| 287 return 1.0f/x; | |
| 288 } | |
| 289 /*- End of function --------------------------------------------------------*/ | |
| 290 | |
| 291 SPAN_DECLARE(float) periodogram_freq_error(const complexf_t *phase_offset, float scale, const complexf_t *last_result, const complexf_t *result) | |
| 292 { | |
| 293 complexf_t prediction; | |
| 294 | |
| 295 /* Rotate the last result by the expected phasor offset to the current result. Then | |
| 296 find the difference between that predicted position, and the actual one. When | |
| 297 scaled by the current signal level, this gives us the frequency error. */ | |
| 298 prediction = complex_mulf(last_result, phase_offset); | |
| 299 return scale*(result->im*prediction.re - result->re*prediction.im)/(result->re*result->re + result->im*result->im); | |
| 300 } | |
| 301 /*- End of function --------------------------------------------------------*/ | |
| 302 /*- End of file ------------------------------------------------------------*/ |
