sip-lab 1.27.1 → 1.28.1
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 +30 -2
- package/binding.gyp +8 -0
- package/build_deps.sh +9 -0
- package/package.json +2 -1
- package/prebuilds/linux-x64/sip-lab.node +0 -0
- package/samples/play_wav_and_speech_recog.bad_transcript.pcmu8000.js +4 -4
- package/samples/speech_synth_and_recog.speex16000.js +6 -6
- package/samples/text_to_speech.js +5 -12
- package/samples_extra/ws_speech_server.dtmf.js +194 -0
- package/samples_extra/ws_speech_server.google.js +190 -0
- package/src/event_templates.cpp +11 -4
- package/src/event_templates.hpp +4 -2
- package/src/pjmedia/include/pjmedia/flite_port.h +2 -2
- package/src/pjmedia/include/pjmedia/ws_speech_port.h +37 -0
- package/src/pjmedia/src/pjmedia/flite_port.c +16 -11
- package/src/pjmedia/src/pjmedia/ws_speech_port.cpp +377 -0
- package/src/sip.cpp +311 -95
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#ifndef __WS_SPEECH_PORT_H__
|
|
2
|
+
#define __WS_SPEECH_PORT_H__
|
|
3
|
+
|
|
4
|
+
#include <pjmedia/port.h>
|
|
5
|
+
#include "websock.h"
|
|
6
|
+
|
|
7
|
+
PJ_BEGIN_DECL
|
|
8
|
+
|
|
9
|
+
enum ws_speech_event
|
|
10
|
+
{
|
|
11
|
+
WS_SPEECH_EVENT_CONNECTED,
|
|
12
|
+
WS_SPEECH_EVENT_CONNECTION_ERROR,
|
|
13
|
+
WS_SPEECH_EVENT_DISCONNECTED,
|
|
14
|
+
WS_SPEECH_EVENT_TEXT_MSG
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
PJ_DEF(pj_status_t) pjmedia_ws_speech_port_create( pj_pool_t *pool,
|
|
18
|
+
unsigned clock_rate,
|
|
19
|
+
unsigned channel_count,
|
|
20
|
+
unsigned samples_per_frame,
|
|
21
|
+
unsigned bits_per_sample,
|
|
22
|
+
pj_websock_endpoint *ws_endpt,
|
|
23
|
+
const char *server_url,
|
|
24
|
+
const char *ss_engine,
|
|
25
|
+
const char *ss_voice,
|
|
26
|
+
const char *ss_language,
|
|
27
|
+
const char *ss_text,
|
|
28
|
+
int ss_times,
|
|
29
|
+
const char *sr_engine,
|
|
30
|
+
const char *sr_language,
|
|
31
|
+
void (*cb)(pjmedia_port*, void *user_data, enum ws_speech_event, char *data, int len),
|
|
32
|
+
void *cb_user_data,
|
|
33
|
+
pjmedia_port **p_port);
|
|
34
|
+
|
|
35
|
+
PJ_END_DECL
|
|
36
|
+
|
|
37
|
+
#endif /* __WS_SPEECH_PORT_H__ */
|
|
@@ -53,7 +53,6 @@ static struct {
|
|
|
53
53
|
|
|
54
54
|
struct flite_t {
|
|
55
55
|
struct pjmedia_port base;
|
|
56
|
-
unsigned options;
|
|
57
56
|
|
|
58
57
|
cst_voice *v;
|
|
59
58
|
unsigned written_samples;
|
|
@@ -61,6 +60,8 @@ struct flite_t {
|
|
|
61
60
|
|
|
62
61
|
pj_bool_t subscribed;
|
|
63
62
|
void (*cb)(pjmedia_port*, void*);
|
|
63
|
+
|
|
64
|
+
int times;
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
#define free_wave(w) if (w) {delete_wave(w) ; w = NULL; }
|
|
@@ -112,6 +113,7 @@ PJ_DEF(pj_status_t) pjmedia_flite_port_create( pj_pool_t *pool,
|
|
|
112
113
|
const char *voice,
|
|
113
114
|
pjmedia_port **p_port)
|
|
114
115
|
{
|
|
116
|
+
printf("pjmedia_flite_port_create\n");
|
|
115
117
|
struct flite_t *flite;
|
|
116
118
|
const pj_str_t name = pj_str("flite_data");
|
|
117
119
|
|
|
@@ -164,13 +166,14 @@ PJ_DEF(pj_status_t) pjmedia_flite_port_create( pj_pool_t *pool,
|
|
|
164
166
|
|
|
165
167
|
PJ_DEF(pj_status_t) pjmedia_flite_port_speak( pjmedia_port *port,
|
|
166
168
|
const char *text,
|
|
167
|
-
|
|
169
|
+
int times) {
|
|
170
|
+
printf("pjmedia_flite_port_speak. text=%s times=%i\n", text, times);
|
|
168
171
|
struct flite_t *flite = (struct flite_t*)port;
|
|
169
172
|
if(flite->w) {
|
|
170
173
|
free_wave(flite->w);
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
flite->
|
|
176
|
+
flite->times = times;
|
|
174
177
|
|
|
175
178
|
flite->w = flite_text_to_wave(text, flite->v);
|
|
176
179
|
if ((unsigned)flite->w->sample_rate != PJMEDIA_PIA_SRATE(&port->info)) {
|
|
@@ -185,22 +188,25 @@ PJ_DEF(pj_status_t) pjmedia_flite_port_speak( pjmedia_port *port,
|
|
|
185
188
|
// called when pjmedia needs data to be sent out
|
|
186
189
|
static pj_status_t flite_get_frame(pjmedia_port *port,
|
|
187
190
|
pjmedia_frame *frame) {
|
|
191
|
+
printf("flite_get_frame\n");
|
|
188
192
|
|
|
189
193
|
PJ_ASSERT_RETURN(port && frame, PJ_EINVAL);
|
|
190
194
|
|
|
191
195
|
struct flite_t *flite = (struct flite_t*)port;
|
|
192
196
|
|
|
193
|
-
if(!flite->w) {
|
|
194
|
-
|
|
197
|
+
if(flite->times <= 0 || !flite->w) {
|
|
198
|
+
printf("flite no data\n");
|
|
195
199
|
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
196
200
|
return PJ_SUCCESS;
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
|
|
203
|
+
printf("written_samples=%i num_samples=%i\n", flite->written_samples, flite->w->num_samples);
|
|
200
204
|
if (flite->written_samples + PJMEDIA_PIA_SPF(&port->info) > (unsigned)flite->w->num_samples) {
|
|
201
205
|
printf("flite end of speech\n");
|
|
202
206
|
|
|
203
|
-
|
|
207
|
+
flite->times--;
|
|
208
|
+
|
|
209
|
+
if(flite->times <= 0 && flite->cb) {
|
|
204
210
|
if (!flite->subscribed) {
|
|
205
211
|
pj_status_t status = pjmedia_event_subscribe(NULL, &speech_on_event,
|
|
206
212
|
flite, flite);
|
|
@@ -218,10 +224,9 @@ static pj_status_t flite_get_frame(pjmedia_port *port,
|
|
|
218
224
|
}
|
|
219
225
|
}
|
|
220
226
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if(no_loop) {
|
|
227
|
+
if(flite->times <= 0) {
|
|
224
228
|
free_wave(flite->w);
|
|
229
|
+
flite->w = NULL;
|
|
225
230
|
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
226
231
|
return PJ_SUCCESS;
|
|
227
232
|
} else {
|
|
@@ -232,7 +237,7 @@ static pj_status_t flite_get_frame(pjmedia_port *port,
|
|
|
232
237
|
memcpy(frame->buf, flite->w->samples + flite->written_samples, PJMEDIA_PIA_SPF(&port->info)*2);
|
|
233
238
|
flite->written_samples += PJMEDIA_PIA_SPF(&port->info);
|
|
234
239
|
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
|
235
|
-
|
|
240
|
+
printf("flite data written samples=%i\n", PJMEDIA_PIA_SPF(&port->info));
|
|
236
241
|
|
|
237
242
|
return PJ_SUCCESS;
|
|
238
243
|
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/* $Id: ws_speech_port.c 0000 2024-03-17 mayamatakeshi $ */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
|
|
4
|
+
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
|
|
5
|
+
*
|
|
6
|
+
* This program is free software; you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation; either version 2 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program; if not, write to the Free Software
|
|
18
|
+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
#include <ws_speech_port.h>
|
|
22
|
+
#include <pjmedia/errno.h>
|
|
23
|
+
#include <pjmedia/port.h>
|
|
24
|
+
#include <pj/assert.h>
|
|
25
|
+
#include <pj/pool.h>
|
|
26
|
+
#include <pj/string.h>
|
|
27
|
+
#include <pj/log.h>
|
|
28
|
+
|
|
29
|
+
#include "rapidjson/document.h"
|
|
30
|
+
#include "rapidjson/writer.h"
|
|
31
|
+
#include "rapidjson/stringbuffer.h"
|
|
32
|
+
|
|
33
|
+
#define SIGNATURE PJMEDIA_SIGNATURE('w', 's', 's', 'p')
|
|
34
|
+
#define THIS_FILE "ws_speech_port.c"
|
|
35
|
+
|
|
36
|
+
#if 0
|
|
37
|
+
# define TRACE_(expr) PJ_LOG(4,expr)
|
|
38
|
+
#else
|
|
39
|
+
# define TRACE_(expr)
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
static pj_status_t put_frame(pjmedia_port *this_port,
|
|
43
|
+
pjmedia_frame *frame);
|
|
44
|
+
|
|
45
|
+
static pj_status_t get_frame(pjmedia_port *this_port,
|
|
46
|
+
pjmedia_frame *frame);
|
|
47
|
+
|
|
48
|
+
static pj_status_t on_destroy(pjmedia_port *this_port);
|
|
49
|
+
|
|
50
|
+
#define SPEECH_BUFFER_SIZE 8192
|
|
51
|
+
#define MINIMAl_BUFFERING 3
|
|
52
|
+
|
|
53
|
+
struct ws_speech_t
|
|
54
|
+
{
|
|
55
|
+
struct pjmedia_port base;
|
|
56
|
+
|
|
57
|
+
struct pj_websock_endpoint *ws_endpt;
|
|
58
|
+
|
|
59
|
+
void (*cb)(pjmedia_port*, void*, enum ws_speech_event, char*, int);
|
|
60
|
+
void *cb_user_data;
|
|
61
|
+
|
|
62
|
+
char buffer[SPEECH_BUFFER_SIZE];
|
|
63
|
+
short buffer_top;
|
|
64
|
+
int buffering_count;
|
|
65
|
+
|
|
66
|
+
char transcript[4096];
|
|
67
|
+
|
|
68
|
+
pj_websock_t *wc;
|
|
69
|
+
bool connected;
|
|
70
|
+
|
|
71
|
+
int sample_rate;
|
|
72
|
+
|
|
73
|
+
char *ss_engine;
|
|
74
|
+
char *ss_voice;
|
|
75
|
+
char *ss_language;
|
|
76
|
+
char *ss_text;
|
|
77
|
+
int ss_times;
|
|
78
|
+
|
|
79
|
+
char *sr_engine;
|
|
80
|
+
char *sr_language;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
static pj_bool_t on_connect_complete(pj_websock_t *c, pj_status_t status)
|
|
85
|
+
{
|
|
86
|
+
printf("ws_speech_port on_connect_complete\n");
|
|
87
|
+
|
|
88
|
+
char buf[1000];
|
|
89
|
+
PJ_PERROR(4, (THIS_FILE, status, "%s() %s", __FUNCTION__,
|
|
90
|
+
pj_websock_print(c, buf, sizeof(buf))));
|
|
91
|
+
|
|
92
|
+
struct ws_speech_t *port = (struct ws_speech_t*)pj_websock_get_userdata(c);
|
|
93
|
+
if (status == PJ_SUCCESS) {
|
|
94
|
+
//suppress this as mostly we don't care about it
|
|
95
|
+
//port->cb((pjmedia_port*)port, port->cb_user_data, WS_SPEECH_EVENT_CONNECTED, "{\"evt\": \"connected\"}", 20);
|
|
96
|
+
} else {
|
|
97
|
+
port->cb((pjmedia_port*)port, port->cb_user_data, WS_SPEECH_EVENT_CONNECTION_ERROR, "{\"evt\": \"connection_error\"}", 27);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if(port->ss_engine) {
|
|
101
|
+
rapidjson::Document document;
|
|
102
|
+
document.SetObject();
|
|
103
|
+
|
|
104
|
+
// Obtain the allocator from the document
|
|
105
|
+
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
106
|
+
|
|
107
|
+
// Add the "cmd" member to the document
|
|
108
|
+
document.AddMember("cmd", "start_speech_synth", allocator);
|
|
109
|
+
|
|
110
|
+
// Create the "args" object
|
|
111
|
+
rapidjson::Value args(rapidjson::kObjectType);
|
|
112
|
+
|
|
113
|
+
// Add members to the "args" object
|
|
114
|
+
args.AddMember("sampleRate", port->sample_rate, allocator);
|
|
115
|
+
args.AddMember("engine", rapidjson::Value(port->ss_engine, allocator), allocator);
|
|
116
|
+
args.AddMember("voice", rapidjson::Value(port->ss_voice, allocator), allocator);
|
|
117
|
+
args.AddMember("language", rapidjson::Value(port->ss_language, allocator), allocator);
|
|
118
|
+
args.AddMember("text", rapidjson::Value(port->ss_text, allocator), allocator);
|
|
119
|
+
args.AddMember("times", port->ss_times, allocator);
|
|
120
|
+
|
|
121
|
+
// Add the "args" object to the document
|
|
122
|
+
document.AddMember("args", args, allocator);
|
|
123
|
+
|
|
124
|
+
// Stringify the JSON document
|
|
125
|
+
rapidjson::StringBuffer buffer;
|
|
126
|
+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
|
127
|
+
document.Accept(writer);
|
|
128
|
+
|
|
129
|
+
printf("\nsending cmd: %.*s\n", buffer.GetLength(), buffer.GetString());
|
|
130
|
+
pj_websock_send(c, PJ_WEBSOCK_OP_TEXT, PJ_TRUE, PJ_TRUE, (void*)buffer.GetString(), buffer.GetLength());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if(port->sr_engine) {
|
|
134
|
+
rapidjson::Document document;
|
|
135
|
+
document.SetObject();
|
|
136
|
+
|
|
137
|
+
// Obtain the allocator from the document
|
|
138
|
+
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
139
|
+
|
|
140
|
+
// Add the "cmd" member to the document
|
|
141
|
+
document.AddMember("cmd", "start_speech_recog", allocator);
|
|
142
|
+
|
|
143
|
+
// Create the "args" object
|
|
144
|
+
rapidjson::Value args(rapidjson::kObjectType);
|
|
145
|
+
|
|
146
|
+
// Add members to the "args" object
|
|
147
|
+
args.AddMember("sampleRate", port->sample_rate, allocator);
|
|
148
|
+
args.AddMember("engine", rapidjson::Value(port->sr_engine, allocator), allocator);
|
|
149
|
+
args.AddMember("language", rapidjson::Value(port->sr_language, allocator), allocator);
|
|
150
|
+
|
|
151
|
+
// Add the "args" object to the document
|
|
152
|
+
document.AddMember("args", args, allocator);
|
|
153
|
+
|
|
154
|
+
// Stringify the JSON document
|
|
155
|
+
rapidjson::StringBuffer buffer;
|
|
156
|
+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
|
157
|
+
document.Accept(writer);
|
|
158
|
+
|
|
159
|
+
printf("\nsending cmd: %.*s\n", buffer.GetLength(), buffer.GetString());
|
|
160
|
+
pj_websock_send(c, PJ_WEBSOCK_OP_TEXT, PJ_TRUE, PJ_TRUE, (void*)buffer.GetString(), buffer.GetLength());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
port->connected = true;
|
|
164
|
+
|
|
165
|
+
return PJ_TRUE;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static pj_bool_t on_rx_msg(pj_websock_t *c,
|
|
169
|
+
pj_websock_rx_data *msg,
|
|
170
|
+
pj_status_t status)
|
|
171
|
+
{
|
|
172
|
+
pj_websock_frame_hdr *hdr;
|
|
173
|
+
char *data;
|
|
174
|
+
char buf[2048];
|
|
175
|
+
|
|
176
|
+
struct ws_speech_t *port = (struct ws_speech_t*)pj_websock_get_userdata(c);
|
|
177
|
+
|
|
178
|
+
if (status != PJ_SUCCESS) {
|
|
179
|
+
PJ_PERROR(2, (THIS_FILE, status, "#Disconnect with %s",
|
|
180
|
+
pj_websock_print(c, buf, sizeof(buf))));
|
|
181
|
+
|
|
182
|
+
port->cb((pjmedia_port*)port, port->cb_user_data, WS_SPEECH_EVENT_DISCONNECTED, "{\"evt\": \"disconnected\"}", 23);
|
|
183
|
+
return PJ_FALSE;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
hdr = &msg->hdr;
|
|
187
|
+
data = (char *)msg->data;
|
|
188
|
+
|
|
189
|
+
if (hdr->opcode == PJ_WEBSOCK_OP_TEXT) {
|
|
190
|
+
printf(
|
|
191
|
+
"RX from %s:\n"
|
|
192
|
+
"TEXT %s %llu/%llu/%llu [%.*s]",
|
|
193
|
+
pj_websock_print(c, buf, sizeof(buf)),
|
|
194
|
+
hdr->mask ? "(masked)" : "", hdr->len, msg->has_read,
|
|
195
|
+
msg->data_len, (int)msg->data_len, data);
|
|
196
|
+
|
|
197
|
+
if(port->cb) {
|
|
198
|
+
port->cb((pjmedia_port*)port, port->cb_user_data, WS_SPEECH_EVENT_TEXT_MSG, data, msg->data_len);
|
|
199
|
+
}
|
|
200
|
+
} else if (hdr->opcode == PJ_WEBSOCK_OP_BIN) {
|
|
201
|
+
printf("PJ_WEBSOCK_OP_BIN. top=%i data_len=%i\n", port->buffer_top, msg->data_len);
|
|
202
|
+
if(port->buffer_top + msg->data_len < SPEECH_BUFFER_SIZE) {
|
|
203
|
+
memcpy(port->buffer + port->buffer_top, data, msg->data_len);
|
|
204
|
+
port->buffer_top += msg->data_len;
|
|
205
|
+
port->buffering_count++;
|
|
206
|
+
}
|
|
207
|
+
} else if (hdr->opcode == PJ_WEBSOCK_OP_PING) {
|
|
208
|
+
PJ_LOG(4, (THIS_FILE, "RX from %s PING",
|
|
209
|
+
pj_websock_print(c, buf, sizeof(buf))));
|
|
210
|
+
/* response pong */
|
|
211
|
+
pj_websock_send(c, PJ_WEBSOCK_OP_PONG, PJ_TRUE, PJ_TRUE, NULL, 0);
|
|
212
|
+
} else if (hdr->opcode == PJ_WEBSOCK_OP_PONG) {
|
|
213
|
+
PJ_LOG(4, (THIS_FILE, "RX from %s PONG",
|
|
214
|
+
pj_websock_print(c, buf, sizeof(buf))));
|
|
215
|
+
} else if (hdr->opcode == PJ_WEBSOCK_OP_CLOSE) {
|
|
216
|
+
PJ_LOG(4, (THIS_FILE, "RX from %s CLOSE",
|
|
217
|
+
pj_websock_print(c, buf, sizeof(buf))));
|
|
218
|
+
pj_websock_close(c, PJ_WEBSOCK_SC_GOING_AWAY, NULL);
|
|
219
|
+
port->cb((pjmedia_port*)port, port->cb_user_data, WS_SPEECH_EVENT_DISCONNECTED, "{\"evt\": \"disconnected\"}", 23);
|
|
220
|
+
return PJ_FALSE; /* Must return false to stop read any more */
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return PJ_TRUE;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static void on_state_change(pj_websock_t *c, int state)
|
|
227
|
+
{
|
|
228
|
+
char buf[1000];
|
|
229
|
+
printf("%s() %s %s", __FUNCTION__, pj_websock_print(c, buf, sizeof(buf)), pj_websock_state_str(state));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
PJ_DEF(pj_status_t) pjmedia_ws_speech_port_create(pj_pool_t *pool,
|
|
234
|
+
unsigned clock_rate,
|
|
235
|
+
unsigned channel_count,
|
|
236
|
+
unsigned samples_per_frame,
|
|
237
|
+
unsigned bits_per_sample,
|
|
238
|
+
struct pj_websock_endpoint *ws_endpt,
|
|
239
|
+
const char *server_url,
|
|
240
|
+
const char *ss_engine,
|
|
241
|
+
const char *ss_voice,
|
|
242
|
+
const char *ss_language,
|
|
243
|
+
const char *ss_text,
|
|
244
|
+
int ss_times,
|
|
245
|
+
const char *sr_engine,
|
|
246
|
+
const char *sr_language,
|
|
247
|
+
void (*cb)(pjmedia_port*, void *user_data, enum ws_speech_event, char *data, int len),
|
|
248
|
+
void *cb_user_data,
|
|
249
|
+
pjmedia_port **p_port)
|
|
250
|
+
{
|
|
251
|
+
printf("pjmedia_ws_speech_port_create clock_rate=%i samples_per_frame=%i bits_per_sample=%i\n", clock_rate, samples_per_frame, bits_per_sample);
|
|
252
|
+
struct ws_speech_t *port;
|
|
253
|
+
const pj_str_t name = pj_str("ws_speech");
|
|
254
|
+
|
|
255
|
+
PJ_ASSERT_RETURN(pool && clock_rate && channel_count == 1 &&
|
|
256
|
+
samples_per_frame && bits_per_sample == 16 &&
|
|
257
|
+
p_port != NULL, PJ_EINVAL);
|
|
258
|
+
|
|
259
|
+
PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
|
|
260
|
+
|
|
261
|
+
PJ_ASSERT_RETURN(cb, PJ_EINVAL);
|
|
262
|
+
|
|
263
|
+
port = PJ_POOL_ZALLOC_T(pool, struct ws_speech_t);
|
|
264
|
+
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
|
|
265
|
+
|
|
266
|
+
port->sample_rate = clock_rate;
|
|
267
|
+
|
|
268
|
+
if(ss_engine) {
|
|
269
|
+
port->ss_engine = (char*)pj_pool_alloc(pool, strlen(ss_engine) + 1);
|
|
270
|
+
pj_ansi_strcpy(port->ss_engine, ss_engine);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if(ss_voice) {
|
|
274
|
+
port->ss_voice = (char*)pj_pool_alloc(pool, strlen(ss_voice) + 1);
|
|
275
|
+
pj_ansi_strcpy(port->ss_voice, ss_voice);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if(ss_language) {
|
|
279
|
+
port->ss_language = (char*)pj_pool_alloc(pool, strlen(ss_language) + 1);
|
|
280
|
+
pj_ansi_strcpy(port->ss_language, ss_language);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if(ss_text) {
|
|
284
|
+
port->ss_text = (char*)pj_pool_alloc(pool, strlen(ss_text) + 1);
|
|
285
|
+
pj_ansi_strcpy(port->ss_text, ss_text);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
port->ss_times = ss_times;
|
|
289
|
+
|
|
290
|
+
if(sr_engine) {
|
|
291
|
+
port->sr_engine = (char*)pj_pool_alloc(pool, strlen(sr_engine) + 1);
|
|
292
|
+
pj_ansi_strcpy(port->sr_engine, sr_engine);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if(sr_language) {
|
|
296
|
+
port->sr_language = (char*)pj_pool_alloc(pool, strlen(sr_language) + 1);
|
|
297
|
+
pj_ansi_strcpy(port->sr_language, sr_language);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, clock_rate,
|
|
301
|
+
channel_count, bits_per_sample, samples_per_frame);
|
|
302
|
+
|
|
303
|
+
port->base.put_frame = &put_frame;
|
|
304
|
+
port->base.get_frame = &get_frame;
|
|
305
|
+
port->base.on_destroy = &on_destroy;
|
|
306
|
+
|
|
307
|
+
port->ws_endpt = ws_endpt,
|
|
308
|
+
|
|
309
|
+
port->cb = cb;
|
|
310
|
+
port->cb_user_data = cb_user_data;
|
|
311
|
+
|
|
312
|
+
pj_websock_http_hdr hdr;
|
|
313
|
+
pj_websock_cb ws_cb;
|
|
314
|
+
pj_bzero(&cb, sizeof(ws_cb));
|
|
315
|
+
ws_cb.on_connect_complete = on_connect_complete;
|
|
316
|
+
ws_cb.on_rx_msg = on_rx_msg;
|
|
317
|
+
ws_cb.on_tx_msg = NULL;
|
|
318
|
+
ws_cb.on_state_change = on_state_change;
|
|
319
|
+
|
|
320
|
+
{
|
|
321
|
+
hdr.key = pj_str("Sec-WebSocket-Protocol");
|
|
322
|
+
hdr.val = pj_str("pjsip");
|
|
323
|
+
pj_websock_connect(port->ws_endpt, server_url, &ws_cb, port, &hdr, 1, &port->wc);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
printf("ws_speech port created: %u/%u/%u/%u", clock_rate, channel_count, samples_per_frame, bits_per_sample);
|
|
327
|
+
|
|
328
|
+
*p_port = &port->base;
|
|
329
|
+
return PJ_SUCCESS;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
static pj_status_t put_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
|
|
333
|
+
if(frame->type != PJMEDIA_FRAME_TYPE_AUDIO) return PJ_SUCCESS;
|
|
334
|
+
|
|
335
|
+
struct ws_speech_t *port = (struct ws_speech_t*) this_port;
|
|
336
|
+
|
|
337
|
+
if(port->wc && port->connected) {
|
|
338
|
+
pj_websock_send(port->wc, PJ_WEBSOCK_OP_BIN, PJ_TRUE, PJ_TRUE, frame->buf, frame->size);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return PJ_SUCCESS;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame) {
|
|
345
|
+
printf("pjmedia_ws_speech_port get_frame\n");
|
|
346
|
+
PJ_ASSERT_RETURN(this_port && frame, PJ_EINVAL);
|
|
347
|
+
|
|
348
|
+
struct ws_speech_t *port = (struct ws_speech_t*)this_port;
|
|
349
|
+
|
|
350
|
+
if(!port->wc) {
|
|
351
|
+
//printf("no data\n");
|
|
352
|
+
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
353
|
+
return PJ_SUCCESS;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
int len = PJMEDIA_PIA_SPF(&this_port->info)*2;
|
|
357
|
+
|
|
358
|
+
if(port->buffering_count >= MINIMAl_BUFFERING && port->buffer_top > 0 && port->buffer_top >= len) {
|
|
359
|
+
printf("get_frame top=%i\n", port->buffer_top);
|
|
360
|
+
memcpy(frame->buf, port->buffer, len);
|
|
361
|
+
port->buffer_top -= len;
|
|
362
|
+
memcpy(port->buffer, port->buffer + len, port->buffer_top);
|
|
363
|
+
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
|
|
364
|
+
} else {
|
|
365
|
+
frame->type = PJMEDIA_FRAME_TYPE_NONE;
|
|
366
|
+
}
|
|
367
|
+
return PJ_SUCCESS;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static pj_status_t on_destroy(pjmedia_port *this_port)
|
|
371
|
+
{
|
|
372
|
+
struct ws_speech_t *port = (struct ws_speech_t*) this_port;
|
|
373
|
+
|
|
374
|
+
return PJ_SUCCESS;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|