sip-lab 1.2.4
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/README.md +38 -0
- package/a.js +280 -0
- package/binding.gyp +101 -0
- package/devjournal +435 -0
- package/index.js +68 -0
- package/install.sh +32 -0
- package/package.json +30 -0
- package/samples/late_negotiation.js +278 -0
- package/samples/simple.js +280 -0
- package/samples/sip_cancel.js +111 -0
- package/src/Makefile +42 -0
- package/src/README +3 -0
- package/src/addon.cpp +1418 -0
- package/src/event_templates.cpp +55 -0
- package/src/event_templates.hpp +27 -0
- package/src/idmanager.cpp +76 -0
- package/src/idmanager.hpp +26 -0
- package/src/log.cpp +18 -0
- package/src/log.hpp +15 -0
- package/src/packetdumper.cpp +234 -0
- package/src/packetdumper.hpp +67 -0
- package/src/pjmedia/Makefile +37 -0
- package/src/pjmedia/devjournal +26 -0
- package/src/pjmedia/include/chainlink/README +3 -0
- package/src/pjmedia/include/chainlink/chainlink.h +11 -0
- package/src/pjmedia/include/chainlink/chainlink_dtmfdet.h +56 -0
- package/src/pjmedia/include/chainlink/chainlink_tonegen.h +178 -0
- package/src/pjmedia/include/chainlink/chainlink_wav_port.h +231 -0
- package/src/pjmedia/include/chainlink/chainlink_wire_port.h +50 -0
- package/src/pjmedia/include/pjmedia/README +3 -0
- package/src/pjmedia/include/pjmedia/dtmfdet.h +74 -0
- package/src/pjmedia/src/chainlink/chainlink_dtmfdet.c +125 -0
- package/src/pjmedia/src/chainlink/chainlink_tonegen.c +901 -0
- package/src/pjmedia/src/chainlink/chainlink_wav_player.c +688 -0
- package/src/pjmedia/src/chainlink/chainlink_wav_writer.c +442 -0
- package/src/pjmedia/src/chainlink/chainlink_wire_port.c +93 -0
- package/src/pjmedia/src/pjmedia/dtmfdet.c +129 -0
- package/src/pjmedia/src/pjmedia/simpleua_dtmfdet.c +753 -0
- package/src/pjmedia/src/pjmedia/tonegen_dtmfdet.c +263 -0
- package/src/sip.cpp +4891 -0
- package/src/sip.hpp +64 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
#include "chainlink.h"
|
|
2
|
+
#include "chainlink_tonegen.h"
|
|
3
|
+
|
|
4
|
+
#include <pjmedia/errno.h>
|
|
5
|
+
#include <pjmedia/silencedet.h>
|
|
6
|
+
#include <pj/assert.h>
|
|
7
|
+
#include <pj/ctype.h>
|
|
8
|
+
#include <pj/lock.h>
|
|
9
|
+
#include <pj/log.h>
|
|
10
|
+
#include <pj/pool.h>
|
|
11
|
+
|
|
12
|
+
/* amplitude */
|
|
13
|
+
#define AMP PJMEDIA_TONEGEN_VOLUME
|
|
14
|
+
|
|
15
|
+
#ifndef M_PI
|
|
16
|
+
# define M_PI ((DATA)3.141592653589793238462643383279)
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
#if PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_SINE
|
|
20
|
+
#include <math.h>
|
|
21
|
+
#define DATA double
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* This is the good old tone generator using sin().
|
|
25
|
+
* Speed = 1347 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
|
|
26
|
+
* approx. 10.91 MIPS
|
|
27
|
+
*
|
|
28
|
+
* 506,535 usec/100.29 MIPS on ARM926EJ-S.
|
|
29
|
+
*/
|
|
30
|
+
struct gen
|
|
31
|
+
{
|
|
32
|
+
DATA add;
|
|
33
|
+
DATA c;
|
|
34
|
+
DATA vol;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
#define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A
|
|
38
|
+
#define GEN_SAMP(val,var) val = (short)(sin(var.c * 2 * M_PI) * \
|
|
39
|
+
var.vol); \
|
|
40
|
+
var.c += var.add
|
|
41
|
+
|
|
42
|
+
#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FLOATING_POINT
|
|
43
|
+
#include <math.h>
|
|
44
|
+
#define DATA float
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* Default floating-point based tone generation using sine wave
|
|
48
|
+
* generation from:
|
|
49
|
+
* http://www.musicdsp.org/showone.php?id=10.
|
|
50
|
+
* This produces good quality tone in relatively faster time than
|
|
51
|
+
* the normal sin() generator.
|
|
52
|
+
* Speed = 350 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
|
|
53
|
+
* approx. 2.84 MIPS
|
|
54
|
+
*
|
|
55
|
+
* 18,037 usec/3.57 MIPS on ARM926EJ-S.
|
|
56
|
+
*/
|
|
57
|
+
struct gen
|
|
58
|
+
{
|
|
59
|
+
DATA a, s0, s1;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
#define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \
|
|
63
|
+
var.s0 = 0; \
|
|
64
|
+
var.s1 = (DATA)(0 - (int)A)
|
|
65
|
+
#define GEN_SAMP(val,var) var.s0 = var.s0 - var.a * var.s1; \
|
|
66
|
+
var.s1 = var.s1 + var.a * var.s0; \
|
|
67
|
+
val = (short) var.s0
|
|
68
|
+
|
|
69
|
+
#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FIXED_POINT_CORDIC
|
|
70
|
+
/* Cordic algorithm with 28 bit size, from:
|
|
71
|
+
* http://www.dcs.gla.ac.uk/~jhw/cordic/
|
|
72
|
+
* Speed = 742 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
|
|
73
|
+
* (PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP=7)
|
|
74
|
+
* approx. 6.01 MIPS
|
|
75
|
+
*
|
|
76
|
+
* ARM926EJ-S results:
|
|
77
|
+
* loop=7: 8,943 usec/1.77 MIPS
|
|
78
|
+
* loop=8: 9,872 usec/1.95 MIPS
|
|
79
|
+
* loop=10: 11,662 usec/2.31 MIPS
|
|
80
|
+
* loop=12: 13,561 usec/2.69 MIPS
|
|
81
|
+
*/
|
|
82
|
+
#define CORDIC_1K 0x026DD3B6
|
|
83
|
+
#define CORDIC_HALF_PI 0x06487ED5
|
|
84
|
+
#define CORDIC_PI (CORDIC_HALF_PI * 2)
|
|
85
|
+
#define CORDIC_MUL_BITS 26
|
|
86
|
+
#define CORDIC_MUL (1 << CORDIC_MUL_BITS)
|
|
87
|
+
#define CORDIC_NTAB 28
|
|
88
|
+
#define CORDIC_LOOP PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP
|
|
89
|
+
|
|
90
|
+
static int cordic_ctab [] =
|
|
91
|
+
{
|
|
92
|
+
0x03243F6A, 0x01DAC670, 0x00FADBAF, 0x007F56EA, 0x003FEAB7,
|
|
93
|
+
0x001FFD55, 0x000FFFAA, 0x0007FFF5, 0x0003FFFE, 0x0001FFFF,
|
|
94
|
+
0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF,
|
|
95
|
+
0x000007FF, 0x000003FF, 0x000001FF, 0x000000FF, 0x0000007F,
|
|
96
|
+
0x0000003F, 0x0000001F, 0x0000000F, 0x00000007, 0x00000003,
|
|
97
|
+
0x00000001, 0x00000000, 0x00000000
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
static pj_int32_t cordic(pj_int32_t theta, unsigned n)
|
|
101
|
+
{
|
|
102
|
+
unsigned k;
|
|
103
|
+
int d;
|
|
104
|
+
pj_int32_t tx;
|
|
105
|
+
pj_int32_t x = CORDIC_1K, y = 0, z = theta;
|
|
106
|
+
|
|
107
|
+
for (k=0; k<n; ++k) {
|
|
108
|
+
#if 0
|
|
109
|
+
d = (z>=0) ? 0 : -1;
|
|
110
|
+
#else
|
|
111
|
+
/* Only slightly (~2.5%) faster, but not portable? */
|
|
112
|
+
d = z>>27;
|
|
113
|
+
#endif
|
|
114
|
+
tx = x - (((y>>k) ^ d) - d);
|
|
115
|
+
y = y + (((x>>k) ^ d) - d);
|
|
116
|
+
z = z - ((cordic_ctab[k] ^ d) - d);
|
|
117
|
+
x = tx;
|
|
118
|
+
}
|
|
119
|
+
return y;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Note: theta must be uint32 here */
|
|
123
|
+
static pj_int32_t cordic_sin(pj_uint32_t theta, unsigned n)
|
|
124
|
+
{
|
|
125
|
+
if (theta < CORDIC_HALF_PI)
|
|
126
|
+
return cordic(theta, n);
|
|
127
|
+
else if (theta < CORDIC_PI)
|
|
128
|
+
return cordic(CORDIC_HALF_PI-(theta-CORDIC_HALF_PI), n);
|
|
129
|
+
else if (theta < CORDIC_PI + CORDIC_HALF_PI)
|
|
130
|
+
return -cordic(theta - CORDIC_PI, n);
|
|
131
|
+
else if (theta < 2 * CORDIC_PI)
|
|
132
|
+
return -cordic(CORDIC_HALF_PI-(theta-3*CORDIC_HALF_PI), n);
|
|
133
|
+
else {
|
|
134
|
+
pj_assert(!"Invalid cordic_sin() value");
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
struct gen
|
|
140
|
+
{
|
|
141
|
+
unsigned add;
|
|
142
|
+
pj_uint32_t c;
|
|
143
|
+
unsigned vol;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
#define VOL(var,v) (((v) * var.vol) >> 15)
|
|
147
|
+
#define GEN_INIT(var,R,F,A) gen_init(&var, R, F, A)
|
|
148
|
+
#define GEN_SAMP(val,var) val = gen_samp(&var)
|
|
149
|
+
|
|
150
|
+
static void gen_init(struct gen *var, unsigned R, unsigned F, unsigned A)
|
|
151
|
+
{
|
|
152
|
+
var->add = 2*CORDIC_PI/R * F;
|
|
153
|
+
var->c = 0;
|
|
154
|
+
var->vol = A;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
PJ_INLINE(short) gen_samp(struct gen *var)
|
|
158
|
+
{
|
|
159
|
+
pj_int32_t val;
|
|
160
|
+
val = cordic_sin(var->c, CORDIC_LOOP);
|
|
161
|
+
/*val = (val * 32767) / CORDIC_MUL;
|
|
162
|
+
*val = VOL((*var), val);
|
|
163
|
+
*/
|
|
164
|
+
val = ((val >> 10) * var->vol) >> 16;
|
|
165
|
+
var->c += var->add;
|
|
166
|
+
if (var->c > 2*CORDIC_PI)
|
|
167
|
+
var->c -= (2 * CORDIC_PI);
|
|
168
|
+
return (short) val;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FAST_FIXED_POINT
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
* Fallback algorithm when floating point is disabled.
|
|
175
|
+
* This is a very fast fixed point tone generation using sine wave
|
|
176
|
+
* approximation from
|
|
177
|
+
* http://www.audiomulch.com/~rossb/code/sinusoids/
|
|
178
|
+
* Quality wise not so good, but it's blazing fast!
|
|
179
|
+
* Speed = 117 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
|
|
180
|
+
* approx. 0.95 MIPS
|
|
181
|
+
*
|
|
182
|
+
* 1,449 usec/0.29 MIPS on ARM926EJ-S.
|
|
183
|
+
*/
|
|
184
|
+
PJ_INLINE(int) approximate_sin3(unsigned x)
|
|
185
|
+
{
|
|
186
|
+
unsigned s=-(int)(x>>31);
|
|
187
|
+
x+=x;
|
|
188
|
+
x=x>>16;
|
|
189
|
+
x*=x^0xffff; // x=x*(2-x)
|
|
190
|
+
x+=x; // optional
|
|
191
|
+
return x^s;
|
|
192
|
+
}
|
|
193
|
+
struct gen
|
|
194
|
+
{
|
|
195
|
+
unsigned add;
|
|
196
|
+
unsigned c;
|
|
197
|
+
unsigned vol;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
#define MAXI ((unsigned)0xFFFFFFFF)
|
|
201
|
+
#define SIN approximate_sin3
|
|
202
|
+
#define VOL(var,v) (((v) * var.vol) >> 15)
|
|
203
|
+
#define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A
|
|
204
|
+
#define GEN_SAMP(val,var) val = (short) VOL(var,SIN(var.c)>>16); \
|
|
205
|
+
var.c += var.add
|
|
206
|
+
|
|
207
|
+
#else
|
|
208
|
+
#error "PJMEDIA_TONEGEN_ALG is not set correctly"
|
|
209
|
+
#endif
|
|
210
|
+
|
|
211
|
+
struct gen_state
|
|
212
|
+
{
|
|
213
|
+
struct gen tone1;
|
|
214
|
+
struct gen tone2;
|
|
215
|
+
pj_bool_t has_tone2;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
static void init_generate_single_tone(struct gen_state *state,
|
|
220
|
+
unsigned clock_rate,
|
|
221
|
+
unsigned freq,
|
|
222
|
+
unsigned vol)
|
|
223
|
+
{
|
|
224
|
+
GEN_INIT(state->tone1,clock_rate,freq,vol);
|
|
225
|
+
state->has_tone2 = PJ_FALSE;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static void generate_single_tone(struct gen_state *state,
|
|
229
|
+
unsigned channel_count,
|
|
230
|
+
unsigned samples,
|
|
231
|
+
short buf[])
|
|
232
|
+
{
|
|
233
|
+
short *end = buf + samples;
|
|
234
|
+
|
|
235
|
+
if (channel_count==1) {
|
|
236
|
+
|
|
237
|
+
while (buf < end) {
|
|
238
|
+
GEN_SAMP(*buf++, state->tone1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
} else if (channel_count == 2) {
|
|
242
|
+
|
|
243
|
+
while (buf < end) {
|
|
244
|
+
GEN_SAMP(*buf, state->tone1);
|
|
245
|
+
*(buf+1) = *buf;
|
|
246
|
+
buf += 2;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
static void init_generate_dual_tone(struct gen_state *state,
|
|
253
|
+
unsigned clock_rate,
|
|
254
|
+
unsigned freq1,
|
|
255
|
+
unsigned freq2,
|
|
256
|
+
unsigned vol)
|
|
257
|
+
{
|
|
258
|
+
GEN_INIT(state->tone1,clock_rate,freq1,vol);
|
|
259
|
+
GEN_INIT(state->tone2,clock_rate,freq2,vol);
|
|
260
|
+
state->has_tone2 = PJ_TRUE;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
static void generate_dual_tone(struct gen_state *state,
|
|
265
|
+
unsigned channel_count,
|
|
266
|
+
unsigned samples,
|
|
267
|
+
short buf[])
|
|
268
|
+
{
|
|
269
|
+
short *end = buf + samples;
|
|
270
|
+
|
|
271
|
+
if (channel_count==1) {
|
|
272
|
+
int val, val2;
|
|
273
|
+
while (buf < end) {
|
|
274
|
+
GEN_SAMP(val, state->tone1);
|
|
275
|
+
GEN_SAMP(val2, state->tone2);
|
|
276
|
+
*buf++ = (short)((val+val2) >> 1);
|
|
277
|
+
}
|
|
278
|
+
} else if (channel_count == 2) {
|
|
279
|
+
int val, val2;
|
|
280
|
+
while (buf < end) {
|
|
281
|
+
|
|
282
|
+
GEN_SAMP(val, state->tone1);
|
|
283
|
+
GEN_SAMP(val2, state->tone2);
|
|
284
|
+
val = (val + val2) >> 1;
|
|
285
|
+
|
|
286
|
+
*buf++ = (short)val;
|
|
287
|
+
*buf++ = (short)val;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
static void init_generate_tone(struct gen_state *state,
|
|
294
|
+
unsigned clock_rate,
|
|
295
|
+
unsigned freq1,
|
|
296
|
+
unsigned freq2,
|
|
297
|
+
unsigned vol)
|
|
298
|
+
{
|
|
299
|
+
if (freq2)
|
|
300
|
+
init_generate_dual_tone(state, clock_rate, freq1, freq2 ,vol);
|
|
301
|
+
else
|
|
302
|
+
init_generate_single_tone(state, clock_rate, freq1,vol);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
static void generate_tone(struct gen_state *state,
|
|
307
|
+
unsigned channel_count,
|
|
308
|
+
unsigned samples,
|
|
309
|
+
short buf[])
|
|
310
|
+
{
|
|
311
|
+
if (!state->has_tone2)
|
|
312
|
+
generate_single_tone(state, channel_count, samples, buf);
|
|
313
|
+
else
|
|
314
|
+
generate_dual_tone(state, channel_count, samples, buf);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
/****************************************************************************/
|
|
319
|
+
|
|
320
|
+
#define SIGNATURE PJMEDIA_SIGNATURE('L', 't', 'o', 'n')
|
|
321
|
+
#define THIS_FILE "chainlink_tonegen.c"
|
|
322
|
+
|
|
323
|
+
#if 0
|
|
324
|
+
# define TRACE_(expr) PJ_LOG(4,expr)
|
|
325
|
+
#else
|
|
326
|
+
# define TRACE_(expr)
|
|
327
|
+
#endif
|
|
328
|
+
|
|
329
|
+
enum flags
|
|
330
|
+
{
|
|
331
|
+
PJMEDIA_TONE_INITIALIZED = 1,
|
|
332
|
+
PJMEDIA_TONE_ENABLE_FADE = 2
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
struct tonegen
|
|
336
|
+
{
|
|
337
|
+
struct chainlink link;
|
|
338
|
+
|
|
339
|
+
/* options */
|
|
340
|
+
unsigned options;
|
|
341
|
+
unsigned playback_options;
|
|
342
|
+
unsigned fade_in_len; /* fade in for this # of samples */
|
|
343
|
+
unsigned fade_out_len; /* fade out for this # of samples*/
|
|
344
|
+
|
|
345
|
+
/* lock */
|
|
346
|
+
pj_lock_t *lock;
|
|
347
|
+
|
|
348
|
+
/* Digit map */
|
|
349
|
+
pjmedia_tone_digit_map *digit_map;
|
|
350
|
+
|
|
351
|
+
/* Tone generation state */
|
|
352
|
+
struct gen_state state;
|
|
353
|
+
|
|
354
|
+
/* Currently played digits: */
|
|
355
|
+
unsigned count; /* # of digits */
|
|
356
|
+
unsigned cur_digit; /* currently played */
|
|
357
|
+
unsigned dig_samples; /* sample pos in cur digit */
|
|
358
|
+
pjmedia_tone_desc digits[PJMEDIA_TONEGEN_MAX_DIGITS];/* array of digits*/
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
/* Default digit map is DTMF */
|
|
363
|
+
static pjmedia_tone_digit_map digit_map =
|
|
364
|
+
{
|
|
365
|
+
16,
|
|
366
|
+
{
|
|
367
|
+
{ '0', 941, 1336 },
|
|
368
|
+
{ '1', 697, 1209 },
|
|
369
|
+
{ '2', 697, 1336 },
|
|
370
|
+
{ '3', 697, 1477 },
|
|
371
|
+
{ '4', 770, 1209 },
|
|
372
|
+
{ '5', 770, 1336 },
|
|
373
|
+
{ '6', 770, 1477 },
|
|
374
|
+
{ '7', 852, 1209 },
|
|
375
|
+
{ '8', 852, 1336 },
|
|
376
|
+
{ '9', 852, 1477 },
|
|
377
|
+
{ 'a', 697, 1633 },
|
|
378
|
+
{ 'b', 770, 1633 },
|
|
379
|
+
{ 'c', 852, 1633 },
|
|
380
|
+
{ 'd', 941, 1633 },
|
|
381
|
+
{ '*', 941, 1209 },
|
|
382
|
+
{ '#', 941, 1477 },
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
static pj_status_t tonegen_put_frame(pjmedia_port *this_port,
|
|
388
|
+
pjmedia_frame *frame);
|
|
389
|
+
static pj_status_t tonegen_get_frame(pjmedia_port *this_port,
|
|
390
|
+
pjmedia_frame *frame);
|
|
391
|
+
static pj_status_t tonegen_destroy(pjmedia_port *this_port);
|
|
392
|
+
|
|
393
|
+
/*
|
|
394
|
+
* Create an instance of tone generator with the specified parameters.
|
|
395
|
+
* When the tone generator is first created, it will be loaded with the
|
|
396
|
+
* default digit map.
|
|
397
|
+
*/
|
|
398
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_create2(pj_pool_t *pool,
|
|
399
|
+
const pj_str_t *name,
|
|
400
|
+
unsigned clock_rate,
|
|
401
|
+
unsigned channel_count,
|
|
402
|
+
unsigned samples_per_frame,
|
|
403
|
+
unsigned bits_per_sample,
|
|
404
|
+
unsigned options,
|
|
405
|
+
pjmedia_port **p_port)
|
|
406
|
+
{
|
|
407
|
+
const pj_str_t STR_TONE_GEN = pj_str("tonegen");
|
|
408
|
+
struct tonegen *tonegen;
|
|
409
|
+
pj_status_t status;
|
|
410
|
+
|
|
411
|
+
PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
|
|
412
|
+
samples_per_frame && bits_per_sample == 16 &&
|
|
413
|
+
p_port != NULL, PJ_EINVAL);
|
|
414
|
+
|
|
415
|
+
/* Only support mono and stereo */
|
|
416
|
+
PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL);
|
|
417
|
+
|
|
418
|
+
/* Create and initialize port */
|
|
419
|
+
tonegen = PJ_POOL_ZALLOC_T(pool, struct tonegen);
|
|
420
|
+
if (name == NULL || name->slen == 0) name = &STR_TONE_GEN;
|
|
421
|
+
status = pjmedia_port_info_init(&tonegen->link.port.info, name,
|
|
422
|
+
SIGNATURE, clock_rate, channel_count,
|
|
423
|
+
bits_per_sample, samples_per_frame);
|
|
424
|
+
if (status != PJ_SUCCESS)
|
|
425
|
+
return status;
|
|
426
|
+
|
|
427
|
+
tonegen->options = options;
|
|
428
|
+
tonegen->link.port.put_frame = &tonegen_put_frame;
|
|
429
|
+
tonegen->link.port.get_frame = &tonegen_get_frame;
|
|
430
|
+
tonegen->link.port.on_destroy = &tonegen_destroy;
|
|
431
|
+
tonegen->digit_map = &digit_map;
|
|
432
|
+
|
|
433
|
+
tonegen->fade_in_len = PJMEDIA_TONEGEN_FADE_IN_TIME * clock_rate / 1000;
|
|
434
|
+
tonegen->fade_out_len = PJMEDIA_TONEGEN_FADE_OUT_TIME * clock_rate / 1000;
|
|
435
|
+
|
|
436
|
+
/* Lock */
|
|
437
|
+
if (options & PJMEDIA_TONEGEN_NO_LOCK) {
|
|
438
|
+
status = pj_lock_create_null_mutex(pool, "tonegen", &tonegen->lock);
|
|
439
|
+
} else {
|
|
440
|
+
status = pj_lock_create_simple_mutex(pool, "tonegen", &tonegen->lock);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (status != PJ_SUCCESS) {
|
|
444
|
+
return status;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
TRACE_((THIS_FILE, "Chainlink_Tonegen created: %u/%u/%u/%u", clock_rate,
|
|
448
|
+
channel_count, samples_per_frame, bits_per_sample));
|
|
449
|
+
|
|
450
|
+
/* Done */
|
|
451
|
+
*p_port = &tonegen->link.port;
|
|
452
|
+
return PJ_SUCCESS;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_create( pj_pool_t *pool,
|
|
457
|
+
unsigned clock_rate,
|
|
458
|
+
unsigned channel_count,
|
|
459
|
+
unsigned samples_per_frame,
|
|
460
|
+
unsigned bits_per_sample,
|
|
461
|
+
unsigned options,
|
|
462
|
+
pjmedia_port **p_port)
|
|
463
|
+
{
|
|
464
|
+
return chainlink_tonegen_create2(pool, NULL, clock_rate, channel_count,
|
|
465
|
+
samples_per_frame, bits_per_sample,
|
|
466
|
+
options, p_port);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
/*
|
|
471
|
+
* Check if the tone generator is still busy producing some tones.
|
|
472
|
+
*/
|
|
473
|
+
PJ_DEF(pj_bool_t) chainlink_tonegen_is_busy(pjmedia_port *port)
|
|
474
|
+
{
|
|
475
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
476
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_TRUE);
|
|
477
|
+
return tonegen->count != 0;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
/*
|
|
482
|
+
* Instruct the tone generator to stop current processing.
|
|
483
|
+
*/
|
|
484
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_stop(pjmedia_port *port)
|
|
485
|
+
{
|
|
486
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
487
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
488
|
+
|
|
489
|
+
TRACE_((THIS_FILE, "tonegen_stop()"));
|
|
490
|
+
|
|
491
|
+
pj_lock_acquire(tonegen->lock);
|
|
492
|
+
tonegen->count = 0;
|
|
493
|
+
tonegen->cur_digit = 0;
|
|
494
|
+
tonegen->dig_samples = 0;
|
|
495
|
+
pj_lock_release(tonegen->lock);
|
|
496
|
+
|
|
497
|
+
return PJ_SUCCESS;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
/*
|
|
502
|
+
* Instruct the tone generator to stop current processing.
|
|
503
|
+
*/
|
|
504
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_rewind(pjmedia_port *port)
|
|
505
|
+
{
|
|
506
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
507
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
508
|
+
|
|
509
|
+
TRACE_((THIS_FILE, "tonegen_rewind()"));
|
|
510
|
+
|
|
511
|
+
/* Reset back to the first tone */
|
|
512
|
+
pj_lock_acquire(tonegen->lock);
|
|
513
|
+
tonegen->cur_digit = 0;
|
|
514
|
+
tonegen->dig_samples = 0;
|
|
515
|
+
pj_lock_release(tonegen->lock);
|
|
516
|
+
|
|
517
|
+
return PJ_SUCCESS;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
/*
|
|
522
|
+
* Callback to destroy tonegen
|
|
523
|
+
*/
|
|
524
|
+
static pj_status_t tonegen_destroy(pjmedia_port *port)
|
|
525
|
+
{
|
|
526
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
527
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
528
|
+
|
|
529
|
+
TRACE_((THIS_FILE, "tonegen_destroy()"));
|
|
530
|
+
|
|
531
|
+
pj_lock_acquire(tonegen->lock);
|
|
532
|
+
pj_lock_release(tonegen->lock);
|
|
533
|
+
|
|
534
|
+
pj_lock_destroy(tonegen->lock);
|
|
535
|
+
|
|
536
|
+
return PJ_SUCCESS;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
static pj_status_t tonegen_put_frame(pjmedia_port *this_port,
|
|
540
|
+
pjmedia_frame *frame)
|
|
541
|
+
{
|
|
542
|
+
PJ_ASSERT_RETURN(this_port && frame, PJ_EINVAL);
|
|
543
|
+
|
|
544
|
+
struct chainlink *link = (struct chainlink*)this_port;
|
|
545
|
+
PJ_ASSERT_RETURN(link->next, PJ_EINVAL);
|
|
546
|
+
PJ_ASSERT_RETURN(link->next->put_frame, PJ_EINVAL);
|
|
547
|
+
|
|
548
|
+
return link->next->put_frame(link->next, frame);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/*
|
|
552
|
+
* Fill a frame with tones.
|
|
553
|
+
*/
|
|
554
|
+
static pj_status_t tonegen_get_frame(pjmedia_port *port,
|
|
555
|
+
pjmedia_frame *frame)
|
|
556
|
+
{
|
|
557
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
558
|
+
short *dst, *end;
|
|
559
|
+
unsigned clock_rate = PJMEDIA_PIA_SRATE(&tonegen->link.port.info);
|
|
560
|
+
|
|
561
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
562
|
+
|
|
563
|
+
pj_lock_acquire(tonegen->lock);
|
|
564
|
+
|
|
565
|
+
if (tonegen->count == 0) {
|
|
566
|
+
/* We don't have digits to play */
|
|
567
|
+
|
|
568
|
+
//Let's get the fram from downstream port
|
|
569
|
+
struct chainlink *link = (struct chainlink*)port;
|
|
570
|
+
PJ_ASSERT_RETURN(link->next, PJ_EINVAL);
|
|
571
|
+
PJ_ASSERT_RETURN(link->next->get_frame, PJ_EINVAL);
|
|
572
|
+
|
|
573
|
+
link->next->get_frame(link->next, frame);
|
|
574
|
+
|
|
575
|
+
goto on_return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (tonegen->cur_digit > tonegen->count) {
|
|
579
|
+
/* We have played all the digits */
|
|
580
|
+
if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
|
|
581
|
+
{
|
|
582
|
+
/* Reset back to the first tone */
|
|
583
|
+
tonegen->cur_digit = 0;
|
|
584
|
+
tonegen->dig_samples = 0;
|
|
585
|
+
|
|
586
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): rewind"));
|
|
587
|
+
|
|
588
|
+
} else {
|
|
589
|
+
tonegen->count = 0;
|
|
590
|
+
tonegen->cur_digit = 0;
|
|
591
|
+
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
592
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit"));
|
|
593
|
+
goto on_return;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (tonegen->dig_samples>=(tonegen->digits[tonegen->cur_digit].on_msec+
|
|
598
|
+
tonegen->digits[tonegen->cur_digit].off_msec)*
|
|
599
|
+
clock_rate / 1000)
|
|
600
|
+
{
|
|
601
|
+
/* We have finished with current digit */
|
|
602
|
+
tonegen->cur_digit++;
|
|
603
|
+
tonegen->dig_samples = 0;
|
|
604
|
+
|
|
605
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): next digit"));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (tonegen->cur_digit >= tonegen->count) {
|
|
609
|
+
/* After we're finished with the last digit, we have played all
|
|
610
|
+
* the digits
|
|
611
|
+
*/
|
|
612
|
+
if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
|
|
613
|
+
{
|
|
614
|
+
/* Reset back to the first tone */
|
|
615
|
+
tonegen->cur_digit = 0;
|
|
616
|
+
tonegen->dig_samples = 0;
|
|
617
|
+
|
|
618
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): rewind"));
|
|
619
|
+
|
|
620
|
+
} else {
|
|
621
|
+
tonegen->count = 0;
|
|
622
|
+
tonegen->cur_digit = 0;
|
|
623
|
+
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
624
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit"));
|
|
625
|
+
goto on_return;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
dst = (short*) frame->buf;
|
|
630
|
+
end = dst + PJMEDIA_PIA_SPF(&port->info);
|
|
631
|
+
|
|
632
|
+
while (dst < end) {
|
|
633
|
+
pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit];
|
|
634
|
+
unsigned required, cnt, on_samp, off_samp;
|
|
635
|
+
|
|
636
|
+
required = end - dst;
|
|
637
|
+
on_samp = dig->on_msec * clock_rate / 1000;
|
|
638
|
+
off_samp = dig->off_msec * clock_rate / 1000;
|
|
639
|
+
|
|
640
|
+
/* Init tonegen */
|
|
641
|
+
if (tonegen->dig_samples == 0 &&
|
|
642
|
+
(tonegen->count!=1 || !(dig->flags & PJMEDIA_TONE_INITIALIZED)))
|
|
643
|
+
{
|
|
644
|
+
init_generate_tone(&tonegen->state, PJMEDIA_PIA_SRATE(&port->info),
|
|
645
|
+
dig->freq1, dig->freq2, dig->volume);
|
|
646
|
+
dig->flags |= PJMEDIA_TONE_INITIALIZED;
|
|
647
|
+
if (tonegen->cur_digit > 0) {
|
|
648
|
+
/* Clear initialized flag of previous digit */
|
|
649
|
+
tonegen->digits[tonegen->cur_digit-1].flags &=
|
|
650
|
+
(~PJMEDIA_TONE_INITIALIZED);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* Add tone signal */
|
|
655
|
+
if (tonegen->dig_samples < on_samp) {
|
|
656
|
+
cnt = on_samp - tonegen->dig_samples;
|
|
657
|
+
if (cnt > required)
|
|
658
|
+
cnt = required;
|
|
659
|
+
generate_tone(&tonegen->state, PJMEDIA_PIA_CCNT(&port->info),
|
|
660
|
+
cnt, dst);
|
|
661
|
+
|
|
662
|
+
dst += cnt;
|
|
663
|
+
tonegen->dig_samples += cnt;
|
|
664
|
+
required -= cnt;
|
|
665
|
+
|
|
666
|
+
if ((dig->flags & PJMEDIA_TONE_ENABLE_FADE) &&
|
|
667
|
+
tonegen->dig_samples == cnt)
|
|
668
|
+
{
|
|
669
|
+
/* Fade in */
|
|
670
|
+
short *samp = (dst - cnt);
|
|
671
|
+
short *end;
|
|
672
|
+
|
|
673
|
+
if (cnt > tonegen->fade_in_len)
|
|
674
|
+
cnt = tonegen->fade_in_len;
|
|
675
|
+
end = samp + cnt;
|
|
676
|
+
if (cnt) {
|
|
677
|
+
const unsigned step = 0xFFFF / cnt;
|
|
678
|
+
unsigned scale = 0;
|
|
679
|
+
|
|
680
|
+
for (; samp < end; ++samp) {
|
|
681
|
+
(*samp) = (short)(((*samp) * scale) >> 16);
|
|
682
|
+
scale += step;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} else if ((dig->flags & PJMEDIA_TONE_ENABLE_FADE) &&
|
|
686
|
+
tonegen->dig_samples==on_samp)
|
|
687
|
+
{
|
|
688
|
+
/* Fade out */
|
|
689
|
+
if (cnt > tonegen->fade_out_len)
|
|
690
|
+
cnt = tonegen->fade_out_len;
|
|
691
|
+
if (cnt) {
|
|
692
|
+
short *samp = (dst - cnt);
|
|
693
|
+
const unsigned step = 0xFFFF / cnt;
|
|
694
|
+
unsigned scale = 0xFFFF - step;
|
|
695
|
+
|
|
696
|
+
for (; samp < dst; ++samp) {
|
|
697
|
+
(*samp) = (short)(((*samp) * scale) >> 16);
|
|
698
|
+
scale -= step;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (dst == end)
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* Add silence signal */
|
|
708
|
+
cnt = off_samp + on_samp - tonegen->dig_samples;
|
|
709
|
+
if (cnt > required)
|
|
710
|
+
cnt = required;
|
|
711
|
+
pjmedia_zero_samples(dst, cnt);
|
|
712
|
+
dst += cnt;
|
|
713
|
+
tonegen->dig_samples += cnt;
|
|
714
|
+
|
|
715
|
+
/* Move to next digit if we're finished with this tone */
|
|
716
|
+
if (tonegen->dig_samples >= on_samp + off_samp) {
|
|
717
|
+
tonegen->cur_digit++;
|
|
718
|
+
tonegen->dig_samples = 0;
|
|
719
|
+
|
|
720
|
+
if (tonegen->cur_digit >= tonegen->count) {
|
|
721
|
+
/* All digits have been played */
|
|
722
|
+
if ((tonegen->options & PJMEDIA_TONEGEN_LOOP) ||
|
|
723
|
+
(tonegen->playback_options & PJMEDIA_TONEGEN_LOOP))
|
|
724
|
+
{
|
|
725
|
+
tonegen->cur_digit = 0;
|
|
726
|
+
} else {
|
|
727
|
+
break;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (dst < end)
|
|
734
|
+
pjmedia_zero_samples(dst, end-dst);
|
|
735
|
+
|
|
736
|
+
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
|
737
|
+
frame->size = PJMEDIA_PIA_AVG_FSZ(&port->info);
|
|
738
|
+
|
|
739
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): frame created, level=%u",
|
|
740
|
+
pjmedia_calc_avg_signal((pj_int16_t*)frame->buf, frame->size/2)));
|
|
741
|
+
|
|
742
|
+
if (tonegen->cur_digit >= tonegen->count) {
|
|
743
|
+
if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
|
|
744
|
+
{
|
|
745
|
+
/* Reset back to the first tone */
|
|
746
|
+
tonegen->cur_digit = 0;
|
|
747
|
+
tonegen->dig_samples = 0;
|
|
748
|
+
|
|
749
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): rewind"));
|
|
750
|
+
|
|
751
|
+
} else {
|
|
752
|
+
tonegen->count = 0;
|
|
753
|
+
tonegen->cur_digit = 0;
|
|
754
|
+
|
|
755
|
+
TRACE_((THIS_FILE, "tonegen_get_frame(): no more digit"));
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
on_return:
|
|
760
|
+
pj_lock_release(tonegen->lock);
|
|
761
|
+
return PJ_SUCCESS;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
/*
|
|
766
|
+
* Play tones.
|
|
767
|
+
*/
|
|
768
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_play( pjmedia_port *port,
|
|
769
|
+
unsigned count,
|
|
770
|
+
const pjmedia_tone_desc tones[],
|
|
771
|
+
unsigned options)
|
|
772
|
+
{
|
|
773
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
774
|
+
unsigned i;
|
|
775
|
+
|
|
776
|
+
PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE &&
|
|
777
|
+
count && tones, PJ_EINVAL);
|
|
778
|
+
|
|
779
|
+
/* Don't put more than available buffer */
|
|
780
|
+
PJ_ASSERT_RETURN(count+tonegen->count <= PJMEDIA_TONEGEN_MAX_DIGITS,
|
|
781
|
+
PJ_ETOOMANY);
|
|
782
|
+
|
|
783
|
+
pj_lock_acquire(tonegen->lock);
|
|
784
|
+
|
|
785
|
+
/* Set playback options */
|
|
786
|
+
tonegen->playback_options = options;
|
|
787
|
+
|
|
788
|
+
/* Copy digits */
|
|
789
|
+
pj_memcpy(tonegen->digits + tonegen->count,
|
|
790
|
+
tones, count * sizeof(pjmedia_tone_desc));
|
|
791
|
+
|
|
792
|
+
/* Normalize volume, and check if we need to disable fading.
|
|
793
|
+
* Disable fading if tone off time is zero. Application probably
|
|
794
|
+
* wants to play this tone continuously (e.g. dial tone).
|
|
795
|
+
*/
|
|
796
|
+
for (i=0; i<count; ++i) {
|
|
797
|
+
pjmedia_tone_desc *t = &tonegen->digits[i+tonegen->count];
|
|
798
|
+
if (t->volume == 0)
|
|
799
|
+
t->volume = AMP;
|
|
800
|
+
else if (t->volume < 0)
|
|
801
|
+
t->volume = (short) -t->volume;
|
|
802
|
+
/* Reset flags */
|
|
803
|
+
t->flags = 0;
|
|
804
|
+
if (t->off_msec != 0)
|
|
805
|
+
t->flags |= PJMEDIA_TONE_ENABLE_FADE;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
tonegen->count += count;
|
|
809
|
+
|
|
810
|
+
pj_lock_release(tonegen->lock);
|
|
811
|
+
|
|
812
|
+
return PJ_SUCCESS;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
/*
|
|
817
|
+
* Play digits.
|
|
818
|
+
*/
|
|
819
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_play_digits( pjmedia_port *port,
|
|
820
|
+
unsigned count,
|
|
821
|
+
const pjmedia_tone_digit digits[],
|
|
822
|
+
unsigned options)
|
|
823
|
+
{
|
|
824
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
825
|
+
pjmedia_tone_desc tones[PJMEDIA_TONEGEN_MAX_DIGITS];
|
|
826
|
+
const pjmedia_tone_digit_map *map;
|
|
827
|
+
unsigned i;
|
|
828
|
+
|
|
829
|
+
PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE &&
|
|
830
|
+
count && digits, PJ_EINVAL);
|
|
831
|
+
PJ_ASSERT_RETURN(count < PJMEDIA_TONEGEN_MAX_DIGITS, PJ_ETOOMANY);
|
|
832
|
+
|
|
833
|
+
pj_lock_acquire(tonegen->lock);
|
|
834
|
+
|
|
835
|
+
map = tonegen->digit_map;
|
|
836
|
+
|
|
837
|
+
for (i=0; i<count; ++i) {
|
|
838
|
+
int d = pj_tolower(digits[i].digit);
|
|
839
|
+
unsigned j;
|
|
840
|
+
|
|
841
|
+
/* Translate ASCII digits with digitmap */
|
|
842
|
+
for (j=0; j<map->count; ++j) {
|
|
843
|
+
if (d == map->digits[j].digit)
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
if (j == map->count) {
|
|
847
|
+
pj_lock_release(tonegen->lock);
|
|
848
|
+
return PJMEDIA_RTP_EINDTMF;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
tones[i].freq1 = map->digits[j].freq1;
|
|
852
|
+
tones[i].freq2 = map->digits[j].freq2;
|
|
853
|
+
tones[i].on_msec = digits[i].on_msec;
|
|
854
|
+
tones[i].off_msec = digits[i].off_msec;
|
|
855
|
+
tones[i].volume = digits[i].volume;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
pj_lock_release(tonegen->lock);
|
|
859
|
+
|
|
860
|
+
return chainlink_tonegen_play(port, count, tones, options);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
/*
|
|
865
|
+
* Get the digit-map currently used by this tone generator.
|
|
866
|
+
*/
|
|
867
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_get_digit_map(pjmedia_port *port,
|
|
868
|
+
const pjmedia_tone_digit_map **m)
|
|
869
|
+
{
|
|
870
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
871
|
+
|
|
872
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
873
|
+
PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL);
|
|
874
|
+
|
|
875
|
+
*m = tonegen->digit_map;
|
|
876
|
+
|
|
877
|
+
return PJ_SUCCESS;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
/*
|
|
882
|
+
* Set digit map to be used by the tone generator.
|
|
883
|
+
*/
|
|
884
|
+
PJ_DEF(pj_status_t) chainlink_tonegen_set_digit_map(pjmedia_port *port,
|
|
885
|
+
pjmedia_tone_digit_map *m)
|
|
886
|
+
{
|
|
887
|
+
struct tonegen *tonegen = (struct tonegen*) port;
|
|
888
|
+
|
|
889
|
+
PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
|
|
890
|
+
PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL);
|
|
891
|
+
|
|
892
|
+
pj_lock_acquire(tonegen->lock);
|
|
893
|
+
|
|
894
|
+
tonegen->digit_map = m;
|
|
895
|
+
|
|
896
|
+
pj_lock_release(tonegen->lock);
|
|
897
|
+
|
|
898
|
+
return PJ_SUCCESS;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
|