sip-lab 1.40.1 → 1.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,18 +18,34 @@
18
18
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
19
  */
20
20
 
21
+ /*
22
+ * BFSK detector variant 2: uses spandsp's Goertzel implementation.
23
+ *
24
+ * spandsp's goertzel_result() returns the squared DFT magnitude for the
25
+ * configured frequency. For a full-scale sine wave of N samples the
26
+ * expected result is approximately:
27
+ *
28
+ * ((N * 32768.0 / sqrt(2)) ^ 2 [float build]
29
+ *
30
+ * We derive a per-instance threshold from the reference level at creation
31
+ * time so the detector scales correctly with block size and sample rate.
32
+ */
33
+
21
34
  #include <pjmedia/bfsk_det2.h>
22
35
  #include <pjmedia/errno.h>
23
36
  #include <pjmedia/port.h>
24
37
  #include <pj/assert.h>
25
38
  #include <pj/pool.h>
26
39
  #include <pj/string.h>
40
+ #include <pj/log.h>
27
41
 
28
42
  #include <spandsp.h>
29
43
  #include <spandsp/expose.h>
30
44
  #include <spandsp/tone_detect.h>
31
45
 
32
46
  #include <math.h>
47
+ #include <stdio.h>
48
+ #include <stdlib.h>
33
49
 
34
50
  #define SIGNATURE PJMEDIA_SIGNATURE('b', 'f', 'd', '2')
35
51
  #define THIS_FILE "bfsk_det2.c"
@@ -40,68 +56,113 @@
40
56
  # define TRACE_(expr)
41
57
  #endif
42
58
 
43
- #include <stdio.h>
44
- #include <stdlib.h>
45
- #include <stdbool.h>
59
+ /*
60
+ * Number of samples fed to each Goertzel block.
61
+ * 102 samples @ 8000 Hz = ~12.75 ms per block, ~78 Hz resolution.
62
+ * This matches spandsp's DTMF detector block size and is a good trade-off
63
+ * between latency and frequency selectivity for telephony FSK.
64
+ */
65
+ #define SAMPLES_PER_BLOCK 102
66
+
67
+ /*
68
+ * Detection threshold expressed as a fraction of the full-scale reference
69
+ * magnitude. 0.05 = the tone must carry at least 5% of full-scale energy
70
+ * to be declared present. Increase toward 1.0 for noisier environments.
71
+ */
72
+ #define THRESHOLD_RATIO 0.05f
46
73
 
47
74
  static pj_status_t bfsk_det2_put_frame(pjmedia_port *this_port,
48
- pjmedia_frame *frame);
75
+ pjmedia_frame *frame);
49
76
  static pj_status_t bfsk_det2_on_destroy(pjmedia_port *this_port);
50
77
 
51
78
  struct bfsk_det2
52
79
  {
53
- struct pjmedia_port base;
80
+ struct pjmedia_port base;
54
81
  int clock_rate;
55
82
  int freq_zero;
56
83
  int freq_one;
57
84
 
85
+ /*
86
+ * *_in_progress: set when the corresponding tone was above threshold in
87
+ * the previous block. A bit is reported on the trailing edge.
88
+ * Mutual exclusion is enforced by the dominant-frequency logic.
89
+ */
58
90
  int zero_in_progress;
59
91
  int one_in_progress;
60
92
 
61
- int32_t threshold;
62
- int32_t energy;
63
-
93
+ /* spandsp Goertzel descriptors (static configuration) and states. */
64
94
  goertzel_descriptor_t desc_zero;
65
95
  goertzel_descriptor_t desc_one;
96
+ goertzel_state_t state_zero;
97
+ goertzel_state_t state_one;
66
98
 
67
- goertzel_state_t *goertzel_zero;
68
- goertzel_state_t *goertzel_one;
99
+ /*
100
+ * Threshold in the same units as goertzel_result().
101
+ * Computed at create time as THRESHOLD_RATIO * reference_magnitude.
102
+ */
103
+ #if defined(SPANDSP_USE_FIXED_POINT)
104
+ int32_t threshold;
105
+ #else
106
+ float threshold;
107
+ #endif
69
108
 
70
109
  void (*bfsk_cb)(pjmedia_port*, void*, int);
71
110
  void *bfsk_cb_user_data;
72
111
  };
73
112
 
74
- #define DTMF_SAMPLES_PER_BLOCK 102
113
+ /*
114
+ * Compute the Goertzel result for a synthetic full-scale pure sine wave
115
+ * at the given frequency so we can derive a calibrated threshold.
116
+ */
117
+ #if defined(SPANDSP_USE_FIXED_POINT)
118
+ static int32_t compute_reference_magnitude(int freq, int sample_rate)
119
+ #else
120
+ static float compute_reference_magnitude(int freq, int sample_rate)
121
+ #endif
122
+ {
123
+ goertzel_descriptor_t desc;
124
+ goertzel_state_t state;
125
+ double t = 0.0;
126
+ double tstep = 1.0 / sample_rate;
127
+ int i;
128
+
129
+ make_goertzel_descriptor(&desc, (float)freq, SAMPLES_PER_BLOCK);
130
+ goertzel_init(&state, &desc);
131
+
132
+ for (i = 0; i < SAMPLES_PER_BLOCK; i++) {
133
+ int16_t s = (int16_t)(32767.0 * sin(2.0 * M_PI * freq * t));
134
+ goertzel_sample(&state, s);
135
+ t += tstep;
136
+ }
137
+
138
+ return goertzel_result(&state);
139
+ }
75
140
 
76
141
  PJ_DEF(pj_status_t) pjmedia_bfsk_det2_create( pj_pool_t *pool,
77
- unsigned clock_rate,
78
- unsigned channel_count,
79
- unsigned samples_per_frame,
80
- unsigned bits_per_sample,
81
- void (*cb)(pjmedia_port*,
82
- void *user_data,
83
- int bit),
84
- void *user_data,
142
+ unsigned clock_rate,
143
+ unsigned channel_count,
144
+ unsigned samples_per_frame,
145
+ unsigned bits_per_sample,
146
+ void (*cb)(pjmedia_port*,
147
+ void *user_data,
148
+ int bit),
149
+ void *user_data,
85
150
  int freq_zero,
86
151
  int freq_one,
87
- pjmedia_port **p_port)
152
+ pjmedia_port **p_port)
88
153
  {
89
- //printf("pjmedia_bfsk_det2_create\n");
90
154
  struct bfsk_det2 *det;
91
-
92
155
  const pj_str_t name = pj_str("bfsk_det2");
93
156
 
94
157
  PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
95
- samples_per_frame && bits_per_sample == 16 &&
96
- p_port != NULL, PJ_EINVAL);
97
-
98
- PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
158
+ samples_per_frame && bits_per_sample == 16 &&
159
+ p_port != NULL, PJ_EINVAL);
99
160
 
100
161
  det = PJ_POOL_ZALLOC_T(pool, struct bfsk_det2);
101
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
162
+ PJ_ASSERT_RETURN(det != NULL, PJ_ENOMEM);
102
163
 
103
164
  pjmedia_port_info_init(&det->base.info, &name, SIGNATURE, clock_rate,
104
- channel_count, bits_per_sample, samples_per_frame);
165
+ channel_count, bits_per_sample, samples_per_frame);
105
166
 
106
167
  det->base.put_frame = &bfsk_det2_put_frame;
107
168
  det->base.on_destroy = &bfsk_det2_on_destroy;
@@ -113,108 +174,108 @@ PJ_DEF(pj_status_t) pjmedia_bfsk_det2_create( pj_pool_t *pool,
113
174
  det->freq_zero = freq_zero;
114
175
  det->freq_one = freq_one;
115
176
 
116
- make_goertzel_descriptor(&det->desc_zero, freq_zero, DTMF_SAMPLES_PER_BLOCK);
117
- make_goertzel_descriptor(&det->desc_one, freq_one, DTMF_SAMPLES_PER_BLOCK);
177
+ /* Initialise spandsp Goertzel descriptors and states. */
178
+ make_goertzel_descriptor(&det->desc_zero, (float)freq_zero, SAMPLES_PER_BLOCK);
179
+ make_goertzel_descriptor(&det->desc_one, (float)freq_one, SAMPLES_PER_BLOCK);
118
180
 
119
- goertzel_init(&det->goertzel_zero, &det->desc_zero);
120
- goertzel_init(&det->goertzel_one, &det->desc_one);
181
+ goertzel_init(&det->state_zero, &det->desc_zero);
182
+ goertzel_init(&det->state_one, &det->desc_one);
121
183
 
122
- int sample_rate = clock_rate;
123
-
124
- //printf("bfsk_det2: clock_rate=%u channel_count=%u samples_per_frame=%u bits_per_frame=%u", clock_rate, channel_count, samples_per_frame, bits_per_sample);
125
-
126
- goertzel_det_init(&det->goertzel_zero, det->freq_zero, sample_rate);
127
- goertzel_det_init(&det->goertzel_one, det->freq_one, sample_rate);
184
+ /*
185
+ * Derive threshold from the reference magnitude of each frequency.
186
+ * Use the average of the two reference levels so a single threshold
187
+ * applies to both detectors.
188
+ */
189
+ #if defined(SPANDSP_USE_FIXED_POINT)
190
+ {
191
+ int32_t ref0 = compute_reference_magnitude(freq_zero, (int)clock_rate);
192
+ int32_t ref1 = compute_reference_magnitude(freq_one, (int)clock_rate);
193
+ det->threshold = (int32_t)(THRESHOLD_RATIO * (float)((ref0 + ref1) / 2));
194
+ }
195
+ #else
196
+ {
197
+ float ref0 = compute_reference_magnitude(freq_zero, (int)clock_rate);
198
+ float ref1 = compute_reference_magnitude(freq_one, (int)clock_rate);
199
+ det->threshold = THRESHOLD_RATIO * ((ref0 + ref1) / 2.0f);
200
+ }
201
+ #endif
128
202
 
129
- *p_port = &det->base;
203
+ *p_port = &det->base;
130
204
  return PJ_SUCCESS;
131
205
  }
132
206
 
133
207
  static pj_status_t bfsk_det2_put_frame(pjmedia_port *this_port,
134
- pjmedia_frame *frame)
208
+ pjmedia_frame *frame)
135
209
  {
136
- //printf("bfsk_det2 put_frame\n");
137
- if(frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
210
+ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
138
211
 
139
212
  struct bfsk_det2 *dport = (struct bfsk_det2*) this_port;
140
213
 
141
- int size = frame->size;
142
- int bps = PJMEDIA_PIA_BITS(&dport->base.info);
143
-
144
- //printf("p=%x, size=%i clock_rate=%i bits_per_sample=%i\n", frame->buf, size, dport->clock_rate, bps);
145
-
146
- int16_t * samples = (int16_t*)frame->buf;
147
- int16_t * num_samples = frame->size/2;
214
+ int16_t *samples = (int16_t*)frame->buf;
215
+ int num_samples = (int)(frame->size / sizeof(int16_t));
216
+ int pos;
148
217
 
149
218
  /*
150
- printf("Buffer contents:\n");
151
- for (int i = 0; i < size; i++) {
152
- printf("%02x", samples[i] & 0xFF);
153
- printf("%02x", samples[i] >> 8 & 0xFF);
154
- }
155
- printf("\n");
156
- */
219
+ * Feed samples into the Goertzel filters one SAMPLES_PER_BLOCK chunk at
220
+ * a time. Any trailing samples that don't fill a complete block are
221
+ * discarded this matches how spandsp's own tone detectors behave and
222
+ * avoids carrying partial state across frames.
223
+ */
224
+ for (pos = 0; pos + SAMPLES_PER_BLOCK <= num_samples; pos += SAMPLES_PER_BLOCK) {
225
+ int j;
226
+ for (j = 0; j < SAMPLES_PER_BLOCK; j++) {
227
+ goertzel_sample(&dport->state_zero, samples[pos + j]);
228
+ goertzel_sample(&dport->state_one, samples[pos + j]);
229
+ }
157
230
 
158
231
  #if defined(SPANDSP_USE_FIXED_POINT)
159
- int32_t row_energy[4];
160
- int32_t col_energy[4];
161
- int16_t xamp;
162
- float famp;
232
+ int32_t zero_power = goertzel_result(&dport->state_zero);
233
+ int32_t one_power = goertzel_result(&dport->state_one);
163
234
  #else
164
- float row_energy[4];
165
- float col_energy[4];
166
- float xamp;
167
- float famp;
235
+ float zero_power = goertzel_result(&dport->state_zero);
236
+ float one_power = goertzel_result(&dport->state_one);
168
237
  #endif
169
238
 
170
- float v1;
171
- int i;
172
- int j;
173
- int sample;
174
- int limit;
175
- uint8_t hit;
239
+ /* goertzel_result() calls goertzel_reset() internally, so the
240
+ * state is ready for the next block without further action. */
241
+
242
+ /*
243
+ * FSK: at most one frequency should be active at a time.
244
+ * When both exceed the threshold pick the stronger one.
245
+ */
246
+ int zero, one;
247
+ if (zero_power >= dport->threshold || one_power >= dport->threshold) {
248
+ if (zero_power >= one_power) {
249
+ zero = 1;
250
+ one = 0;
251
+ } else {
252
+ zero = 0;
253
+ one = 1;
254
+ }
255
+ } else {
256
+ zero = 0;
257
+ one = 0;
258
+ }
176
259
 
177
- for (sample = 0; sample < num_samples; sample = limit)
178
- {
179
- limit = num_samples;
180
- for (j = sample; j < limit; j++)
181
- {
182
- xamp = samples[j];
183
- xamp = goertzel_preadjust_amp(xamp);
184
- #if defined(SPANDSP_USE_FIXED_POINT)
185
- dport->energy += ((int32_t) xamp*xamp);
186
- #else
187
- dport->energy += xamp*xamp;
188
- #endif
189
- goertzel_samplex(&dport->goertzel_zero, xamp);
190
- goertzel_samplex(&dport->goertzel_one, xamp);
260
+ TRACE_((THIS_FILE, "zero_power=%f one_power=%f threshold=%f zero=%d one=%d",
261
+ (double)zero_power, (double)one_power, (double)dport->threshold,
262
+ zero, one));
263
+
264
+ /* Report bit=0 on trailing edge of zero tone. */
265
+ if (dport->zero_in_progress && !zero) {
266
+ dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 0);
267
+ dport->zero_in_progress = 0;
268
+ } else {
269
+ dport->zero_in_progress = zero;
191
270
  }
192
- }
193
- int32_t zero_power = goertzel_result(&dport->goertzel_zero);
194
- int32_t one_power = goertzel_result(&dport->goertzel_one);
195
-
196
- int zero = zero_power > dport->threshold;
197
- int one = one_power > dport->threshold;
198
-
199
- printf("zero_power=%f zero_in_progress=%i zero=%i threshold=%f\n", zero_power, dport->zero_in_progress, zero, dport->threshold);
200
- printf(" one_power=%f one_in_progress=%i one=%i threshold=%f\n", one_power, dport->one_in_progress, one, dport->threshold);
201
-
202
- // Check for zero signal extinction
203
- if(dport->zero_in_progress && zero == 0) {
204
- printf("notifying bit=0\n");
205
- dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 0);
206
- dport->zero_in_progress = 0;
207
- } else {
208
- dport->zero_in_progress = zero;
209
- }
210
271
 
211
- // Check for one signal extinction
212
- if(dport->one_in_progress && one == 0) {
213
- printf("notifying bit=1\n");
214
- dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 1);
215
- dport->one_in_progress = 0;
216
- } else {
217
- dport->one_in_progress = one;
272
+ /* Report bit=1 on trailing edge of one tone. */
273
+ if (dport->one_in_progress && !one) {
274
+ dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 1);
275
+ dport->one_in_progress = 0;
276
+ } else {
277
+ dport->one_in_progress = one;
278
+ }
218
279
  }
219
280
 
220
281
  return PJ_SUCCESS;
@@ -222,5 +283,6 @@ static pj_status_t bfsk_det2_put_frame(pjmedia_port *this_port,
222
283
 
223
284
  static pj_status_t bfsk_det2_on_destroy(pjmedia_port *this_port)
224
285
  {
286
+ PJ_UNUSED_ARG(this_port);
225
287
  return PJ_SUCCESS;
226
288
  }
Binary file
Binary file