sip-lab 1.41.0 → 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.
package/DEV.md CHANGED
@@ -181,5 +181,19 @@ Then do the actual publish:
181
181
  ```
182
182
  npm publish
183
183
  ```
184
+ If you get something like
185
+ ```
186
+ npm timing command:publish Completed in 8775ms
187
+ npm ERR! code E404
188
+ npm ERR! 404 Not Found - PUT https://registry.npmjs.org/sip-lab - Not found
189
+ npm ERR! 404
190
+ npm ERR! 404 'sip-lab@1.41.0' is not in this registry.
191
+
192
+ ```
193
+ it means you are not logged in.
194
+ So first do:
195
+ ```
196
+ npm login
197
+ ```
184
198
 
185
199
 
package/README.md CHANGED
@@ -11,31 +11,24 @@ See [Documentation](https://github.com/MayamaTakeshi/sip-lab/blob/master/DOC.md)
11
11
 
12
12
  ## Installation
13
13
 
14
- The npm package is built for Debian 11 and this is the recommended distro.
15
-
16
- You can use other debian/ubuntu version but they will require a build of dependencies that will take time (something like 7 minutes but this was measured on my slow PC).
14
+ The npm package is built for Ubuntu/Debian and might work with other linux distros.
17
15
 
18
16
  First install apt packages:
19
17
 
20
18
  ```
21
- apt install build-essential automake autoconf libtool libspeex-dev libopus-dev libsdl2-dev libavdevice-dev libswscale-dev libv4l-dev libopencore-amrnb-dev libopencore-amrwb-dev libvo-amrwbenc-dev libvo-amrwbenc-dev libboost-dev libtiff-dev libpcap-dev libssl-dev uuid-dev flite-dev cmake git wget
19
+ apt install build-essential automake autoconf libtool libspeex-dev libopus-dev libsdl2-dev libavdevice-dev libswscale-dev libv4l-dev libopencore-amrnb-dev libopencore-amrwb-dev libvo-amrwbenc-dev libvo-amrwbenc-dev libboost-dev libtiff-dev libpcap-dev libssl-dev uuid-dev flite-dev cmake git wget bc
22
20
 
23
21
  ```
22
+ Obs: in ubuntu you might also need to install libssl1.1
24
23
 
25
- Then switch to node v19, switch to your node project folder and install sip-lab:
26
-
24
+ Then:
27
25
  ```
28
- nvm install 19
29
- nvm use 19
30
- cd YOUR_NODE_PROJECT_FOLDER
31
26
  npm i sip-lab
32
27
  ```
33
- Obs: once you install sip-lab, you can switch to other node versions like v20, v21.
34
-
35
-
36
28
  Then run some sample script from subfolder samples:
37
29
  ```
38
- node node_modules/sip-lab/samples/simple.js
30
+ cd node_modules/sip-lab
31
+ node ./samples/simple.js
39
32
  ```
40
33
 
41
34
  The above script has detailed comments.
package/index.js CHANGED
@@ -74,7 +74,7 @@ addon.call = {
74
74
  stop_inband_dtmf_detection: (c_id, params) => { return addon.call_stop_inband_dtmf_detection(c_id, JSON.stringify(params ? params : {})) },
75
75
 
76
76
  start_bfsk_detection: (c_id, params) => { return addon.call_start_bfsk_detection(c_id, JSON.stringify(params ? params : {})) },
77
- stop_bfsk_dtmf_detection: (c_id, params) => { return addon.call_stop_bfsk_detection(c_id, JSON.stringify(params ? params : {})) },
77
+ stop_bfsk_detection: (c_id, params) => { return addon.call_stop_bfsk_detection(c_id, JSON.stringify(params ? params : {})) },
78
78
 
79
79
  start_speech_recog: (c_id, params) => {
80
80
  var ps = {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sip-lab",
3
- "version": "1.41.0",
3
+ "version": "1.41.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "engines": {
Binary file
@@ -26,6 +26,8 @@
26
26
  #include <pj/string.h>
27
27
 
28
28
  #include <math.h>
29
+ #include <stdio.h>
30
+ #include <stdlib.h>
29
31
 
30
32
  #define SIGNATURE PJMEDIA_SIGNATURE('b', 'f', 'd', 't')
31
33
  #define THIS_FILE "bfsk_det.c"
@@ -36,24 +38,51 @@
36
38
  # define TRACE_(expr)
37
39
  #endif
38
40
 
39
- #include <math.h>
40
- #include <stdio.h>
41
- #include <stdlib.h>
42
- #include <stdbool.h>
41
+ /*
42
+ * Goertzel-based BFSK detector.
43
+ *
44
+ * CHUNK_SIZE is the number of samples per Goertzel analysis block.
45
+ * Frequency resolution = sample_rate / CHUNK_SIZE.
46
+ *
47
+ * Trade-off:
48
+ * - Larger CHUNK_SIZE → better frequency resolution, but longer analysis
49
+ * window. If the window is longer than one bit period, adjacent bits
50
+ * bleed into each other and detection fails.
51
+ * - Smaller CHUNK_SIZE → poorer frequency resolution, more false detects.
52
+ *
53
+ * Default signal_duration (on_msec / off_msec) used by send_bfsk is 10 ms.
54
+ * At 8000 Hz that is 80 samples per bit period. We therefore need
55
+ * CHUNK_SIZE well below 80 so that we see several chunks per tone burst.
56
+ * 40 samples = 5 ms per chunk → resolution ~200 Hz, fits ~2 chunks per 10 ms
57
+ * tone. This is a reasonable compromise for typical FSK tone pairs that have
58
+ * at least a few hundred Hz separation (e.g. 500/2000 Hz used in tests).
59
+ */
60
+ #define CHUNK_SIZE 40
43
61
 
44
- // Converted by ChatGPT from https://github.com/hackergrrl/goertzel/blob/master/index.js
62
+ /*
63
+ * THRESHOLD: normalized ratio of detected magnitude to reference magnitude.
64
+ * A value of 0.3 means the incoming tone must be at least 30% as strong as
65
+ * a full-scale sine wave at that frequency. Tune upward to reduce false
66
+ * positives in noisy environments, downward to detect weaker signals.
67
+ */
68
+ #define THRESHOLD 0.3
69
+
70
+ /* Converted by ChatGPT from https://github.com/hackergrrl/goertzel/blob/master/index.js */
45
71
 
46
72
  typedef struct {
47
73
  int freq;
48
74
  int sampleRate;
49
75
  int samplesPerFrame;
50
- double targetMagnitude; // Store the precomputed magnitude
76
+ double targetMagnitude;
51
77
  } goertzel_t;
52
78
 
53
- #define CHUNK_SIZE 16
54
- #define THRESHOLD 0.9
55
-
56
- double precalcMagnitude(int freq, double rate) {
79
+ /*
80
+ * Compute the expected Goertzel magnitude for a full-scale sine wave at
81
+ * the given frequency and sample rate, using CHUNK_SIZE samples. This is
82
+ * used as a normalization reference so that goertzel_mag() returns a
83
+ * dimensionless ratio in [0, 1] for a matching full-amplitude tone.
84
+ */
85
+ static double precalcMagnitude(int freq, double rate) {
57
86
  double t = 0.0;
58
87
  double tstep = 1.0 / rate;
59
88
  double samples[CHUNK_SIZE];
@@ -62,7 +91,7 @@ double precalcMagnitude(int freq, double rate) {
62
91
  t += tstep;
63
92
  }
64
93
 
65
- int k = (int)(0.5 + (CHUNK_SIZE * freq) / rate);
94
+ int k = (int)(0.5 + ((double)CHUNK_SIZE * freq) / rate);
66
95
  double w = (2 * M_PI / CHUNK_SIZE) * k;
67
96
  double c = cos(w);
68
97
  double s = sin(w);
@@ -77,51 +106,39 @@ double precalcMagnitude(int freq, double rate) {
77
106
 
78
107
  double real = q1 - q2 * c;
79
108
  double imaginary = q2 * s;
80
- double magSquared = real * real + imaginary * imaginary;
81
-
82
- return magSquared;
109
+ return real * real + imaginary * imaginary;
83
110
  }
84
111
 
85
- void goertzel_det_init(goertzel_t *g, int freq, int sample_rate) {
112
+ static void goertzel_det_init(goertzel_t *g, int freq, int sample_rate) {
86
113
  g->freq = freq;
87
114
  g->sampleRate = sample_rate;
88
115
  g->samplesPerFrame = CHUNK_SIZE;
89
116
 
90
117
  assert(g->sampleRate >= g->freq * 2);
91
118
 
92
- g->samplesPerFrame = (int)floor(g->samplesPerFrame);
93
-
94
- // Precompute the target magnitude and store it in the struct
95
119
  g->targetMagnitude = precalcMagnitude(g->freq, g->sampleRate);
96
- // printf("Target Magnitude: %f\n", g->targetMagnitude);
97
120
  }
98
121
 
99
- float goertzel_mag(goertzel_t *g, void *samples) {
100
- // Allocate an array of floats to hold normalized samples
122
+ /*
123
+ * Compute the Goertzel magnitude of the given PCM block and return it as a
124
+ * fraction of the reference magnitude. Input is raw 16-bit little-endian
125
+ * PCM; CHUNK_SIZE samples (2*CHUNK_SIZE bytes) are consumed.
126
+ */
127
+ static float goertzel_mag(goertzel_t *g, void *samples) {
101
128
  float float_samples[CHUNK_SIZE];
102
- uint8_t *byteBuffer = (uint8_t *)samples; // Cast the void* to a byte array (uint8_t *)
103
- for (int i = 0; i < CHUNK_SIZE ; i++) {
104
- // Combine two consecutive bytes into a 16-bit signed integer (little-endian)
105
- int16_t sample = byteBuffer[i * 2] | (byteBuffer[i * 2 + 1] << 8);
106
- float_samples[i] = sample * 2.0f / 0x7FFF;
107
- }
108
-
109
- /*
129
+ uint8_t *byteBuffer = (uint8_t *)samples;
110
130
  for (int i = 0; i < CHUNK_SIZE; i++) {
111
- printf("%f,", float_samples[i]);
131
+ int16_t sample = (int16_t)(byteBuffer[i * 2] | (byteBuffer[i * 2 + 1] << 8));
132
+ float_samples[i] = sample / 32767.0f;
112
133
  }
113
- printf("\n");
114
- */
115
-
116
134
 
117
- int k = (int)(0.5f + (CHUNK_SIZE * g->freq) / g->sampleRate);
118
- float w = (2.0f * M_PI / CHUNK_SIZE) * k;
135
+ int k = (int)(0.5f + ((float)CHUNK_SIZE * g->freq) / g->sampleRate);
136
+ float w = (2.0f * (float)M_PI / CHUNK_SIZE) * k;
119
137
  float c = cosf(w);
120
138
  float s = sinf(w);
121
139
  float coeff = 2.0f * c;
122
140
 
123
141
  float q0 = 0.0f, q1 = 0.0f, q2 = 0.0f;
124
-
125
142
  for (int i = 0; i < CHUNK_SIZE; i++) {
126
143
  q0 = coeff * q1 - q2 + float_samples[i];
127
144
  q2 = q1;
@@ -132,23 +149,28 @@ float goertzel_mag(goertzel_t *g, void *samples) {
132
149
  float imaginary = q2 * s;
133
150
  float magSquared = real * real + imaginary * imaginary;
134
151
 
135
- float per = magSquared / g->targetMagnitude;
136
- //printf("(freq=%i) MagSquared=%f targetMagnitude=%f per=%f\n", g->freq, magSquared, g->targetMagnitude, per);
137
- return per;
152
+ return (float)(magSquared / g->targetMagnitude);
138
153
  }
139
154
 
140
155
 
141
156
  static pj_status_t bfsk_det_put_frame(pjmedia_port *this_port,
142
- pjmedia_frame *frame);
157
+ pjmedia_frame *frame);
143
158
  static pj_status_t bfsk_det_on_destroy(pjmedia_port *this_port);
144
159
 
145
160
  struct bfsk_det
146
161
  {
147
- struct pjmedia_port base;
162
+ struct pjmedia_port base;
148
163
  int clock_rate;
149
164
  int freq_zero;
150
165
  int freq_one;
151
166
 
167
+ /*
168
+ * *_in_progress tracks whether the corresponding tone was above threshold
169
+ * in the previous chunk. A bit is reported on the trailing edge (when
170
+ * the tone drops below threshold) so that we emit exactly one event per
171
+ * burst. Only one of zero_in_progress / one_in_progress should be set
172
+ * at a time; the dominant-frequency logic enforces this.
173
+ */
152
174
  int zero_in_progress;
153
175
  int one_in_progress;
154
176
 
@@ -160,34 +182,30 @@ struct bfsk_det
160
182
  };
161
183
 
162
184
  PJ_DEF(pj_status_t) pjmedia_bfsk_det_create( pj_pool_t *pool,
163
- unsigned clock_rate,
164
- unsigned channel_count,
165
- unsigned samples_per_frame,
166
- unsigned bits_per_sample,
167
- void (*cb)(pjmedia_port*,
168
- void *user_data,
169
- int bit),
170
- void *user_data,
185
+ unsigned clock_rate,
186
+ unsigned channel_count,
187
+ unsigned samples_per_frame,
188
+ unsigned bits_per_sample,
189
+ void (*cb)(pjmedia_port*,
190
+ void *user_data,
191
+ int bit),
192
+ void *user_data,
171
193
  int freq_zero,
172
194
  int freq_one,
173
- pjmedia_port **p_port)
195
+ pjmedia_port **p_port)
174
196
  {
175
- //printf("pjmedia_bfsk_det_create\n");
176
197
  struct bfsk_det *det;
177
-
178
198
  const pj_str_t name = pj_str("bfsk_det");
179
199
 
180
200
  PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
181
- samples_per_frame && bits_per_sample == 16 &&
182
- p_port != NULL, PJ_EINVAL);
183
-
184
- PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
201
+ samples_per_frame && bits_per_sample == 16 &&
202
+ p_port != NULL, PJ_EINVAL);
185
203
 
186
204
  det = PJ_POOL_ZALLOC_T(pool, struct bfsk_det);
187
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
205
+ PJ_ASSERT_RETURN(det != NULL, PJ_ENOMEM);
188
206
 
189
207
  pjmedia_port_info_init(&det->base.info, &name, SIGNATURE, clock_rate,
190
- channel_count, bits_per_sample, samples_per_frame);
208
+ channel_count, bits_per_sample, samples_per_frame);
191
209
 
192
210
  det->base.put_frame = &bfsk_det_put_frame;
193
211
  det->base.on_destroy = &bfsk_det_on_destroy;
@@ -199,73 +217,67 @@ PJ_DEF(pj_status_t) pjmedia_bfsk_det_create( pj_pool_t *pool,
199
217
  det->freq_zero = freq_zero;
200
218
  det->freq_one = freq_one;
201
219
 
202
- float threshold = 0.1;
203
-
204
220
  det->goertzel_zero = PJ_POOL_ZALLOC_T(pool, goertzel_t);
205
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
221
+ PJ_ASSERT_RETURN(det->goertzel_zero != NULL, PJ_ENOMEM);
206
222
 
207
223
  det->goertzel_one = PJ_POOL_ZALLOC_T(pool, goertzel_t);
208
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
209
-
210
- int sample_rate = clock_rate;
211
-
212
- //printf("bfsk_det: clock_rate=%u channel_count=%u samples_per_frame=%u bits_per_frame=%u", clock_rate, channel_count, samples_per_frame, bits_per_sample);
224
+ PJ_ASSERT_RETURN(det->goertzel_one != NULL, PJ_ENOMEM);
213
225
 
214
- goertzel_det_init(det->goertzel_zero, det->freq_zero, sample_rate);
215
- goertzel_det_init(det->goertzel_one, det->freq_one, sample_rate);
226
+ goertzel_det_init(det->goertzel_zero, det->freq_zero, (int)clock_rate);
227
+ goertzel_det_init(det->goertzel_one, det->freq_one, (int)clock_rate);
216
228
 
217
- *p_port = &det->base;
229
+ *p_port = &det->base;
218
230
  return PJ_SUCCESS;
219
231
  }
220
232
 
221
233
  static pj_status_t bfsk_det_put_frame(pjmedia_port *this_port,
222
- pjmedia_frame *frame)
234
+ pjmedia_frame *frame)
223
235
  {
224
- //printf("bfsk_det put_frame\n");
225
- if(frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
236
+ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
226
237
 
227
238
  struct bfsk_det *dport = (struct bfsk_det*) this_port;
228
239
 
229
- int size = frame->size;
230
- int bps = PJMEDIA_PIA_BITS(&dport->base.info);
240
+ int size = (int)frame->size;
231
241
 
232
- //printf("p=%x, size=%i clock_rate=%i bits_per_sample=%i\n", frame->buf, size, dport->clock_rate, bps);
233
-
234
- int16_t * samples = (int16_t*)frame->buf;
235
-
236
- /*
237
- printf("Buffer contents:\n");
238
- for (int i = 0; i < size; i++) {
239
- printf("%02x", samples[i] & 0xFF);
240
- printf("%02x", samples[i] >> 8 & 0xFF);
241
- }
242
- printf("\n");
243
- */
244
-
245
- for(int i=0 ; i<size/2/CHUNK_SIZE; i++) {
246
- double zero_power = goertzel_mag(dport->goertzel_zero, &frame->buf[i*2*CHUNK_SIZE]);
247
- double one_power = goertzel_mag(dport->goertzel_one, &frame->buf[i*2*CHUNK_SIZE]);
248
-
249
- int zero = zero_power > THRESHOLD;
250
- int one = one_power > THRESHOLD;
242
+ /* Process CHUNK_SIZE-sample blocks. */
243
+ int num_chunks = size / 2 / CHUNK_SIZE;
244
+ for (int i = 0; i < num_chunks; i++) {
245
+ void *chunk = (uint8_t*)frame->buf + i * 2 * CHUNK_SIZE;
246
+ float zero_power = goertzel_mag(dport->goertzel_zero, chunk);
247
+ float one_power = goertzel_mag(dport->goertzel_one, chunk);
251
248
 
252
249
  /*
253
- printf("zero_power=%f zero_in_progress=%i zero=%i threshold=%f\n", zero_power, dport->zero_in_progress, zero, THRESHOLD);
254
- printf(" one_power=%f one_in_progress=%i one=%i threshold=%f\n", one_power, dport->one_in_progress, one, THRESHOLD);
255
- */
250
+ * For FSK exactly one frequency should be active at a time.
251
+ * If both exceed the threshold, treat the stronger one as active
252
+ * and suppress the other to prevent simultaneous callbacks.
253
+ */
254
+ int zero, one;
255
+ if (zero_power >= THRESHOLD || one_power >= THRESHOLD) {
256
+ if (zero_power >= one_power) {
257
+ zero = 1;
258
+ one = 0;
259
+ } else {
260
+ zero = 0;
261
+ one = 1;
262
+ }
263
+ } else {
264
+ zero = 0;
265
+ one = 0;
266
+ }
267
+
268
+ TRACE_((THIS_FILE, "zero_power=%f one_power=%f zero=%d one=%d",
269
+ zero_power, one_power, zero, one));
256
270
 
257
- // Check for zero signal extinction
258
- if(dport->zero_in_progress && zero == 0) {
259
- printf("notifying bit=0\n");
271
+ /* Report bit=0 on trailing edge of zero tone. */
272
+ if (dport->zero_in_progress && !zero) {
260
273
  dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 0);
261
274
  dport->zero_in_progress = 0;
262
275
  } else {
263
276
  dport->zero_in_progress = zero;
264
277
  }
265
278
 
266
- // Check for one signal extinction
267
- if(dport->one_in_progress && one == 0) {
268
- printf("notifying bit=1\n");
279
+ /* Report bit=1 on trailing edge of one tone. */
280
+ if (dport->one_in_progress && !one) {
269
281
  dport->bfsk_cb((pjmedia_port*)dport, dport->bfsk_cb_user_data, 1);
270
282
  dport->one_in_progress = 0;
271
283
  } else {
@@ -278,5 +290,6 @@ static pj_status_t bfsk_det_put_frame(pjmedia_port *this_port,
278
290
 
279
291
  static pj_status_t bfsk_det_on_destroy(pjmedia_port *this_port)
280
292
  {
293
+ PJ_UNUSED_ARG(this_port);
281
294
  return PJ_SUCCESS;
282
295
  }
@@ -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
  }