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.
- package/DEV.md +19 -11
- package/README.md +6 -13
- package/binding.gyp +2 -1
- package/index.js +1 -1
- package/package.json +8 -3
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/runtests +25 -9
- package/samples/call_with_authentication.js +278 -0
- package/samples/{t → speex.js} +5 -14
- package/src/pjmedia/src/pjmedia/bfsk_det.c +115 -102
- package/src/pjmedia/src/pjmedia/bfsk_det2.c +171 -109
- package/prebuilds/linux-x64/node.abi102.node +0 -0
- package/prebuilds/linux-x64/node.abi108.node +0 -0
- package/prebuilds/linux-x64/node.abi111.node +0 -0
- package/prebuilds/linux-x64/node.abi115.node +0 -0
- package/prebuilds/linux-x64/node.abi120.node +0 -0
- package/prebuilds/linux-x64/node.abi88.node +0 -0
- package/prebuilds/linux-x64/node.abi93.node +0 -0
- package/src/sip.cpp.old +0 -9236
|
@@ -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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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;
|
|
76
|
+
double targetMagnitude;
|
|
51
77
|
} goertzel_t;
|
|
52
78
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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(
|
|
205
|
+
PJ_ASSERT_RETURN(det != NULL, PJ_ENOMEM);
|
|
188
206
|
|
|
189
207
|
pjmedia_port_info_init(&det->base.info, &name, SIGNATURE, clock_rate,
|
|
190
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
215
|
-
goertzel_det_init(det->goertzel_one,
|
|
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
|
-
|
|
234
|
+
pjmedia_frame *frame)
|
|
223
235
|
{
|
|
224
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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
|
-
|
|
258
|
-
if(dport->zero_in_progress && zero
|
|
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
|
-
|
|
267
|
-
if(dport->one_in_progress && one
|
|
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
|
}
|