mirror of
https://github.com/jambonz/freeswitch-modules.git
synced 2026-01-25 02:08:27 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3459188bb6 | |||
| d6e246d84c | |||
| de676ddc81 | |||
| d41bd15816 | |||
| 8bd20703b8 | |||
| 2e553631dc | |||
| 2a94213668 |
@@ -141,6 +141,11 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ap->m_state == LWS_CLIENT_DISCONNECTING) {
|
||||||
|
lwsl_notice("AudioPipe::lws_service_thread race condition: got incoming message while closing the connection.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (lws_frame_is_binary(wsi)) {
|
if (lws_frame_is_binary(wsi)) {
|
||||||
if (ap->is_bidirectional_audio_stream()) {
|
if (ap->is_bidirectional_audio_stream()) {
|
||||||
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), AudioPipe::BINARY, NULL, (char *) in, len);
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), AudioPipe::BINARY, NULL, (char *) in, len);
|
||||||
|
|||||||
+49
-29
@@ -38,45 +38,65 @@ namespace {
|
|||||||
static uint32_t playCount = 0;
|
static uint32_t playCount = 0;
|
||||||
|
|
||||||
switch_status_t processIncomingBinary(private_t* tech_pvt, switch_core_session_t* session, const char* message, size_t dataLength) {
|
switch_status_t processIncomingBinary(private_t* tech_pvt, switch_core_session_t* session, const char* message, size_t dataLength) {
|
||||||
CircularBuffer_t *cBuffer = (CircularBuffer_t *) tech_pvt->circularBuffer;
|
|
||||||
uint8_t* data = reinterpret_cast<uint8_t*>(const_cast<char*>(message));
|
uint8_t* data = reinterpret_cast<uint8_t*>(const_cast<char*>(message));
|
||||||
uint16_t* data_uint16 = reinterpret_cast<uint16_t*>(data);
|
uint16_t* data_uint16 = reinterpret_cast<uint16_t*>(data);
|
||||||
std::vector<uint16_t> pcm_data(data_uint16, data_uint16 + dataLength / sizeof(uint16_t));
|
std::vector<uint16_t> pcm_data(data_uint16, data_uint16 + dataLength / sizeof(uint16_t));
|
||||||
|
|
||||||
|
// resample if necessary
|
||||||
|
try {
|
||||||
|
if (tech_pvt->bidirectional_audio_resampler) {
|
||||||
|
std::vector<int16_t> in(pcm_data.begin(), pcm_data.end());
|
||||||
|
|
||||||
if (tech_pvt->bidirectional_audio_resampler) {
|
std::vector<int16_t> out(dataLength);
|
||||||
std::vector<int16_t> in(pcm_data.begin(), pcm_data.end());
|
spx_uint32_t in_len = pcm_data.size();
|
||||||
|
spx_uint32_t out_len = out.size();
|
||||||
|
speex_resampler_process_interleaved_int(tech_pvt->bidirectional_audio_resampler, in.data(), &in_len, out.data(), &out_len);
|
||||||
|
|
||||||
std::vector<int16_t> out(dataLength);
|
if (out_len > out.size()) {
|
||||||
spx_uint32_t in_len = pcm_data.size();
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Resampler output exceeded maximum buffer size!\n");
|
||||||
spx_uint32_t out_len = out.size();
|
return SWITCH_STATUS_FALSE;
|
||||||
speex_resampler_process_interleaved_int(tech_pvt->bidirectional_audio_resampler, in.data(), &in_len, out.data(), &out_len);
|
}
|
||||||
|
|
||||||
if (out_len > out.size()) {
|
// Resize the pcm_data to match the output length from resampler, and then copy the resampled data into it.
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Resampler output exceeded maximum buffer size!\n");
|
pcm_data.resize(out_len);
|
||||||
|
memcpy(pcm_data.data(), out.data(), out_len * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error resampling incoming binary message: %s\n", e.what());
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
} catch (...) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error resampling incoming binary message\n");
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != tech_pvt->mutex && switch_mutex_trylock(tech_pvt->mutex) == SWITCH_STATUS_SUCCESS) {
|
||||||
|
//switch_mutex_lock(tech_pvt->mutex);
|
||||||
|
CircularBuffer_t *cBuffer = (CircularBuffer_t *) tech_pvt->circularBuffer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Resize the buffer if necessary
|
||||||
|
size_t bytesResampled = pcm_data.size() * sizeof(uint16_t);
|
||||||
|
if (cBuffer->capacity() - cBuffer->size() < bytesResampled / sizeof(uint16_t)) {
|
||||||
|
// If buffer exceeds some max size, you could return SWITCH_STATUS_FALSE to abort the transfer
|
||||||
|
// if (cBuffer->size() + std::max(bytesResampled / sizeof(uint16_t), (size_t)BUFFER_GROW_SIZE) > MAX_BUFFER_SIZE) return SWITCH_STATUS_FALSE;
|
||||||
|
|
||||||
|
cBuffer->set_capacity(cBuffer->size() + std::max(bytesResampled / sizeof(uint16_t), (size_t)BUFFER_GROW_SIZE));
|
||||||
|
}
|
||||||
|
// Push the data into the buffer.
|
||||||
|
cBuffer->insert(cBuffer->end(), pcm_data.begin(), pcm_data.end());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error processing incoming binary message: %s\n", e.what());
|
||||||
|
switch_mutex_unlock(tech_pvt->mutex);
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
} catch (...) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error processing incoming binary message\n");
|
||||||
|
switch_mutex_unlock(tech_pvt->mutex);
|
||||||
return SWITCH_STATUS_FALSE;
|
return SWITCH_STATUS_FALSE;
|
||||||
}
|
}
|
||||||
|
switch_mutex_unlock(tech_pvt->mutex);
|
||||||
// Resize the pcm_data to match the output length from resampler, and then copy the resampled data into it.
|
return SWITCH_STATUS_SUCCESS;
|
||||||
pcm_data.resize(out_len);
|
|
||||||
memcpy(pcm_data.data(), out.data(), out_len * sizeof(int16_t));
|
|
||||||
}
|
}
|
||||||
switch_mutex_lock(tech_pvt->mutex);
|
return SWITCH_STATUS_FALSE;
|
||||||
|
|
||||||
// Resize the buffer if necessary
|
|
||||||
size_t bytesResampled = pcm_data.size() * sizeof(uint16_t);
|
|
||||||
if (cBuffer->capacity() - cBuffer->size() < bytesResampled / sizeof(uint16_t)) {
|
|
||||||
// If buffer exceeds some max size, you could return SWITCH_STATUS_FALSE to abort the transfer
|
|
||||||
// if (cBuffer->size() + std::max(bytesResampled / sizeof(uint16_t), (size_t)BUFFER_GROW_SIZE) > MAX_BUFFER_SIZE) return SWITCH_STATUS_FALSE;
|
|
||||||
|
|
||||||
cBuffer->set_capacity(cBuffer->size() + std::max(bytesResampled / sizeof(uint16_t), (size_t)BUFFER_GROW_SIZE));
|
|
||||||
}
|
|
||||||
// Push the data into the buffer.
|
|
||||||
cBuffer->insert(cBuffer->end(), pcm_data.begin(), pcm_data.end());
|
|
||||||
|
|
||||||
switch_mutex_unlock(tech_pvt->mutex);
|
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void processIncomingMessage(private_t* tech_pvt, switch_core_session_t* session, const char* message) {
|
void processIncomingMessage(private_t* tech_pvt, switch_core_session_t* session, const char* message) {
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ static const char* proxyIP = std::getenv("JAMBONES_HTTP_PROXY_IP");
|
|||||||
static const char* proxyPort = std::getenv("JAMBONES_HTTP_PROXY_PORT");
|
static const char* proxyPort = std::getenv("JAMBONES_HTTP_PROXY_PORT");
|
||||||
static const char* proxyUsername = std::getenv("JAMBONES_HTTP_PROXY_USERNAME");
|
static const char* proxyUsername = std::getenv("JAMBONES_HTTP_PROXY_USERNAME");
|
||||||
static const char* proxyPassword = std::getenv("JAMBONES_HTTP_PROXY_PASSWORD");
|
static const char* proxyPassword = std::getenv("JAMBONES_HTTP_PROXY_PASSWORD");
|
||||||
|
static const bool use_single_connection = switch_true(std::getenv("AZURE_SPEECH_USE_SINGLE_CONNECTION"));
|
||||||
|
|
||||||
class GStreamer {
|
class GStreamer {
|
||||||
public:
|
public:
|
||||||
GStreamer(
|
GStreamer(
|
||||||
const char *sessionId,
|
const char *sessionId,
|
||||||
const char *bugname,
|
const char *bugname,
|
||||||
u_int16_t channels,
|
u_int16_t channels,
|
||||||
char *lang,
|
char *lang,
|
||||||
@@ -246,6 +247,9 @@ public:
|
|||||||
m_recognizer->Recognized += onRecognitionEvent;
|
m_recognizer->Recognized += onRecognitionEvent;
|
||||||
m_recognizer->Canceled += onCanceled;
|
m_recognizer->Canceled += onCanceled;
|
||||||
|
|
||||||
|
// Store the final configuration string
|
||||||
|
m_configuration_string = createConfigurationStr(channels, lang, interim, samples_per_second, region, subscriptionKey, psession);
|
||||||
|
|
||||||
switch_core_session_rwunlock(psession);
|
switch_core_session_rwunlock(psession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +257,10 @@ public:
|
|||||||
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "GStreamer::~GStreamer %p\n", this);
|
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "GStreamer::~GStreamer %p\n", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* configuration() {
|
||||||
|
return m_configuration_string.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
void connect() {
|
void connect() {
|
||||||
if (m_connecting) return;
|
if (m_connecting) return;
|
||||||
m_connecting = true;
|
m_connecting = true;
|
||||||
@@ -304,7 +312,7 @@ public:
|
|||||||
|
|
||||||
void finish() {
|
void finish() {
|
||||||
if (m_finished) return;
|
if (m_finished) return;
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "GStreamer::finish - calling StopContinuousRecognitionAsync (%p)\n", this);
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "GStreamer::finish - calling StopContinuousRecognitionAsync (%p)\n", this);
|
||||||
m_finished = true;
|
m_finished = true;
|
||||||
m_recognizer->StopContinuousRecognitionAsync().get();
|
m_recognizer->StopContinuousRecognitionAsync().get();
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "GStreamer::finish - recognition has completed (%p)\n", this);
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "GStreamer::finish - recognition has completed (%p)\n", this);
|
||||||
@@ -318,13 +326,33 @@ public:
|
|||||||
return m_connecting;
|
return m_connecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasConfigurationChanged(
|
||||||
|
u_int16_t channels,
|
||||||
|
char *lang,
|
||||||
|
int interim,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
const char* region,
|
||||||
|
const char* subscriptionKey) {
|
||||||
|
switch_core_session_t* psession = switch_core_session_locate(m_sessionId.c_str());
|
||||||
|
if (!psession) throw std::invalid_argument( "session id no longer active" );
|
||||||
|
|
||||||
|
std::string newConfiguration = createConfigurationStr(channels, lang, interim, samples_per_second, region, subscriptionKey, psession);
|
||||||
|
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(psession), SWITCH_LOG_DEBUG,
|
||||||
|
"hasConfigurationChanged: old configurattion: %s, new configuration: %s\n", configuration(), newConfiguration.c_str());
|
||||||
|
|
||||||
|
switch_core_session_rwunlock(psession);
|
||||||
|
|
||||||
|
return strcmp(newConfiguration.c_str(), configuration());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_sessionId;
|
std::string m_sessionId;
|
||||||
std::string m_bugname;
|
std::string m_bugname;
|
||||||
std::string m_region;
|
std::string m_region;
|
||||||
std::shared_ptr<SpeechRecognizer> m_recognizer;
|
std::shared_ptr<SpeechRecognizer> m_recognizer;
|
||||||
std::shared_ptr<PushAudioInputStream> m_pushStream;
|
std::shared_ptr<PushAudioInputStream> m_pushStream;
|
||||||
|
std::string m_configuration_string;
|
||||||
responseHandler_t m_responseHandler;
|
responseHandler_t m_responseHandler;
|
||||||
bool m_interim;
|
bool m_interim;
|
||||||
bool m_finished;
|
bool m_finished;
|
||||||
@@ -332,6 +360,64 @@ private:
|
|||||||
bool m_connecting;
|
bool m_connecting;
|
||||||
bool m_stopped;
|
bool m_stopped;
|
||||||
SimpleBuffer m_audioBuffer;
|
SimpleBuffer m_audioBuffer;
|
||||||
|
|
||||||
|
std::string createConfigurationStr(
|
||||||
|
u_int16_t channels,
|
||||||
|
char *lang,
|
||||||
|
int interim,
|
||||||
|
uint32_t samples_per_second,
|
||||||
|
const char* region,
|
||||||
|
const char* subscriptionKey,
|
||||||
|
switch_core_session_t* psession
|
||||||
|
) {
|
||||||
|
switch_channel_t *channel = switch_core_session_get_channel(psession);
|
||||||
|
std::ostringstream configuration_stream;
|
||||||
|
configuration_stream <<
|
||||||
|
channels << ";" <<
|
||||||
|
lang << ";" <<
|
||||||
|
interim << ";" <<
|
||||||
|
samples_per_second << ";" <<
|
||||||
|
region << ";" <<
|
||||||
|
subscriptionKey << ";";
|
||||||
|
|
||||||
|
const char* endpoint = switch_channel_get_variable(channel, "AZURE_SERVICE_ENDPOINT");
|
||||||
|
const char* endpointId = switch_channel_get_variable(channel, "AZURE_SERVICE_ENDPOINT_ID");
|
||||||
|
configuration_stream << (endpoint ? endpoint : "") << ";"
|
||||||
|
<< (endpointId ? endpointId : "") << ";";
|
||||||
|
if (switch_true(switch_channel_get_variable(channel, "AZURE_USE_OUTPUT_FORMAT_DETAILED"))) {
|
||||||
|
configuration_stream << "output_format_detailed;";
|
||||||
|
}
|
||||||
|
if (switch_true(switch_channel_get_variable(channel, "AZURE_AUDIO_LOGGING"))) {
|
||||||
|
configuration_stream << "audio_logging;";
|
||||||
|
}
|
||||||
|
configuration_stream << (proxyIP ? proxyIP : "") << ";"
|
||||||
|
<< (proxyPort ? proxyPort : "") << ";"
|
||||||
|
<< (proxyUsername ? proxyUsername : "") << ";"
|
||||||
|
<< (proxyPassword ? proxyPassword : "") << ";";
|
||||||
|
const char* var;
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_SPEECH_ALTERNATIVE_LANGUAGE_CODES")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_PROFANITY_OPTION")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_REQUEST_SNR")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_INITIAL_SPEECH_TIMEOUT_MS")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_SPEECH_SEGMENTATION_SILENCE_TIMEOUT_MS")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_LANGUAGE_ID_MODE")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
if (var = switch_channel_get_variable(channel, "AZURE_SPEECH_HINTS")) {
|
||||||
|
configuration_stream << var << ";";
|
||||||
|
}
|
||||||
|
return configuration_stream.str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void reaper(struct cap_cb *cb) {
|
static void reaper(struct cap_cb *cb) {
|
||||||
@@ -388,16 +474,33 @@ extern "C" {
|
|||||||
GStreamer *streamer = NULL;
|
GStreamer *streamer = NULL;
|
||||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||||
|
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, bugname);
|
||||||
|
const char* subscriptionKey = switch_channel_get_variable(channel, "AZURE_SUBSCRIPTION_KEY");
|
||||||
|
const char* region = switch_channel_get_variable(channel, "AZURE_REGION");
|
||||||
|
const char* sessionId = switch_core_session_get_uuid(session);
|
||||||
|
auto read_codec = switch_core_session_get_read_codec(session);
|
||||||
|
uint32_t sampleRate = read_codec->implementation->actual_samples_per_second;
|
||||||
|
if (bug) {
|
||||||
|
struct cap_cb* existing_cb = (struct cap_cb*) switch_core_media_bug_get_user_data(bug);
|
||||||
|
GStreamer* existing_streamer = (GStreamer*) existing_cb->streamer;
|
||||||
|
existing_cb->is_keep_alive = 0;
|
||||||
|
if (!existing_streamer->hasConfigurationChanged(channels, lang, interim, sampleRate, region, subscriptionKey)) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Reuse active azure connection.\n");
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Azure configuration is changed, destroy old and create new azure connection\n");
|
||||||
|
reaper(existing_cb);
|
||||||
|
streamer = new GStreamer(sessionId, bugname, channels, lang, interim, sampleRate, region, subscriptionKey, responseHandler);
|
||||||
|
if (!existing_cb->vad) streamer->connect();
|
||||||
|
existing_cb->streamer = streamer;
|
||||||
|
*ppUserData = existing_cb;
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
int err;
|
int err;
|
||||||
switch_threadattr_t *thd_attr = NULL;
|
switch_threadattr_t *thd_attr = NULL;
|
||||||
switch_memory_pool_t *pool = switch_core_session_get_pool(session);
|
switch_memory_pool_t *pool = switch_core_session_get_pool(session);
|
||||||
auto read_codec = switch_core_session_get_read_codec(session);
|
|
||||||
uint32_t sampleRate = read_codec->implementation->actual_samples_per_second;
|
|
||||||
const char* sessionId = switch_core_session_get_uuid(session);
|
|
||||||
struct cap_cb* cb = (struct cap_cb *) switch_core_session_alloc(session, sizeof(*cb));
|
struct cap_cb* cb = (struct cap_cb *) switch_core_session_alloc(session, sizeof(*cb));
|
||||||
memset(cb, sizeof(cb), 0);
|
memset(cb, sizeof(cb), 0);
|
||||||
const char* subscriptionKey = switch_channel_get_variable(channel, "AZURE_SUBSCRIPTION_KEY");
|
|
||||||
const char* region = switch_channel_get_variable(channel, "AZURE_REGION");
|
|
||||||
cb->channels = channels;
|
cb->channels = channels;
|
||||||
strncpy(cb->sessionId, sessionId, MAX_SESSION_ID);
|
strncpy(cb->sessionId, sessionId, MAX_SESSION_ID);
|
||||||
strncpy(cb->bugname, bugname, MAX_BUG_LEN);
|
strncpy(cb->bugname, bugname, MAX_BUG_LEN);
|
||||||
@@ -418,15 +521,27 @@ extern "C" {
|
|||||||
|
|
||||||
cb->responseHandler = responseHandler;
|
cb->responseHandler = responseHandler;
|
||||||
|
|
||||||
|
cb->interim = interim;
|
||||||
|
strncpy(cb->lang, lang, MAX_LANG);
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s: initializing gstreamer with %s\n",
|
||||||
|
switch_channel_get_name(channel), bugname);
|
||||||
|
streamer = new GStreamer(sessionId, bugname, channels, lang, interim, sampleRate, cb->region, subscriptionKey, responseHandler);
|
||||||
|
cb->streamer = streamer;
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_init: config: %s\n", streamer->configuration());
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s: Error initializing gstreamer: %s.\n",
|
||||||
|
switch_channel_get_name(channel), e.what());
|
||||||
|
return SWITCH_STATUS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (switch_mutex_init(&cb->mutex, SWITCH_MUTEX_NESTED, pool) != SWITCH_STATUS_SUCCESS) {
|
if (switch_mutex_init(&cb->mutex, SWITCH_MUTEX_NESTED, pool) != SWITCH_STATUS_SUCCESS) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error initializing mutex\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error initializing mutex\n");
|
||||||
status = SWITCH_STATUS_FALSE;
|
status = SWITCH_STATUS_FALSE;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb->interim = interim;
|
|
||||||
strncpy(cb->lang, lang, MAX_LANG);
|
|
||||||
|
|
||||||
/* determine if we need to resample the audio to 16-bit 8khz */
|
/* determine if we need to resample the audio to 16-bit 8khz */
|
||||||
if (sampleRate != 8000) {
|
if (sampleRate != 8000) {
|
||||||
cb->resampler = speex_resampler_init(1, sampleRate, 8000, SWITCH_RESAMPLE_QUALITY, &err);
|
cb->resampler = speex_resampler_init(1, sampleRate, 8000, SWITCH_RESAMPLE_QUALITY, &err);
|
||||||
@@ -468,23 +583,10 @@ extern "C" {
|
|||||||
switch_channel_get_name(channel), voice_ms, mode);
|
switch_channel_get_name(channel), voice_ms, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!cb->vad) streamer->connect();
|
||||||
try {
|
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s: initializing gstreamer with %s\n",
|
|
||||||
switch_channel_get_name(channel), bugname);
|
|
||||||
streamer = new GStreamer(sessionId, bugname, channels, lang, interim, sampleRate, cb->region, subscriptionKey, responseHandler);
|
|
||||||
cb->streamer = streamer;
|
|
||||||
if (!cb->vad) streamer->connect();
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s: Error initializing gstreamer: %s.\n",
|
|
||||||
switch_channel_get_name(channel), e.what());
|
|
||||||
return SWITCH_STATUS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*ppUserData = cb;
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
*ppUserData = cb;
|
||||||
|
cb->is_keep_alive = 0;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +598,12 @@ extern "C" {
|
|||||||
struct cap_cb *cb = (struct cap_cb *) switch_core_media_bug_get_user_data(bug);
|
struct cap_cb *cb = (struct cap_cb *) switch_core_media_bug_get_user_data(bug);
|
||||||
switch_status_t st;
|
switch_status_t st;
|
||||||
|
|
||||||
|
if (use_single_connection && !channelIsClosing) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_stop: call is running, use_single_connection is true, keep alive is activated\n");
|
||||||
|
cb->is_keep_alive = 1;
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// close connection and get final responses
|
// close connection and get final responses
|
||||||
switch_mutex_lock(cb->mutex);
|
switch_mutex_lock(cb->mutex);
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_stop: locked session\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_stop: locked session\n");
|
||||||
@@ -507,6 +615,7 @@ extern "C" {
|
|||||||
if (streamer) reaper(cb);
|
if (streamer) reaper(cb);
|
||||||
killcb(cb);
|
killcb(cb);
|
||||||
switch_mutex_unlock(cb->mutex);
|
switch_mutex_unlock(cb->mutex);
|
||||||
|
switch_mutex_destroy(cb->mutex);
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_stop: unlocked session\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "azure_transcribe_session_stop: unlocked session\n");
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
return SWITCH_STATUS_SUCCESS;
|
||||||
@@ -524,7 +633,18 @@ extern "C" {
|
|||||||
|
|
||||||
frame.data = data;
|
frame.data = data;
|
||||||
frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
|
frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
|
||||||
|
if (cb->is_keep_alive) {
|
||||||
|
// remove media bug buffered data
|
||||||
|
while (true) {
|
||||||
|
unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0};
|
||||||
|
switch_frame_t frame = { 0 };
|
||||||
|
frame.data = data;
|
||||||
|
frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
|
||||||
|
switch_status_t rv = switch_core_media_bug_read(bug, &frame, SWITCH_TRUE);
|
||||||
|
if (rv != SWITCH_STATUS_SUCCESS) break;
|
||||||
|
}
|
||||||
|
return SWITCH_TRUE;
|
||||||
|
}
|
||||||
if (switch_mutex_trylock(cb->mutex) == SWITCH_STATUS_SUCCESS) {
|
if (switch_mutex_trylock(cb->mutex) == SWITCH_STATUS_SUCCESS) {
|
||||||
GStreamer* streamer = (GStreamer *) cb->streamer;
|
GStreamer* streamer = (GStreamer *) cb->streamer;
|
||||||
if (streamer) {
|
if (streamer) {
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ static switch_status_t start_capture(switch_core_session_t *session, switch_medi
|
|||||||
switch_codec_implementation_t read_impl = { 0 };
|
switch_codec_implementation_t read_impl = { 0 };
|
||||||
void *pUserData;
|
void *pUserData;
|
||||||
uint32_t samples_per_second;
|
uint32_t samples_per_second;
|
||||||
|
int use_single_connection = switch_true(getenv("AZURE_SPEECH_USE_SINGLE_CONNECTION"));
|
||||||
|
bug = switch_channel_get_private(channel, bugname);
|
||||||
if (switch_channel_get_private(channel, bugname)) {
|
if (bug && !use_single_connection) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "removing bug from previous transcribe\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "removing bug from previous transcribe\n");
|
||||||
do_stop(session, bugname);
|
do_stop(session, bugname);
|
||||||
}
|
}
|
||||||
@@ -91,12 +91,13 @@ static switch_status_t start_capture(switch_core_session_t *session, switch_medi
|
|||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error initializing azure speech session.\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error initializing azure speech session.\n");
|
||||||
return SWITCH_STATUS_FALSE;
|
return SWITCH_STATUS_FALSE;
|
||||||
}
|
}
|
||||||
if ((status = switch_core_media_bug_add(session, bugname, NULL, capture_callback, pUserData, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
|
if (!bug || !use_single_connection) {
|
||||||
return status;
|
if ((status = switch_core_media_bug_add(session, bugname, NULL, capture_callback, pUserData, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
switch_channel_set_private(channel, bugname, bug);
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "added media bug for azure transcribe\n");
|
||||||
}
|
}
|
||||||
switch_channel_set_private(channel, bugname, bug);
|
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "added media bug for azure transcribe\n");
|
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
return SWITCH_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ struct cap_cb {
|
|||||||
char lang[MAX_LANG];
|
char lang[MAX_LANG];
|
||||||
char region[MAX_REGION];
|
char region[MAX_REGION];
|
||||||
|
|
||||||
|
int is_keep_alive;
|
||||||
|
|
||||||
switch_vad_t * vad;
|
switch_vad_t * vad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,13 @@ typedef boost::circular_buffer<uint16_t> CircularBuffer_t;
|
|||||||
|
|
||||||
using namespace Microsoft::CognitiveServices::Speech;
|
using namespace Microsoft::CognitiveServices::Speech;
|
||||||
|
|
||||||
|
static const char* audioLogFile= std::getenv("AZURE_AUDIO_LOGGING");
|
||||||
|
|
||||||
static std::string fullDirPath;
|
static std::string fullDirPath;
|
||||||
|
|
||||||
static void start_synthesis(std::shared_ptr<SpeechSynthesizer> speechSynthesizer, const char* text, azure_t* a) {
|
static void start_synthesis(std::shared_ptr<SpeechSynthesizer> speechSynthesizer, const char* text, azure_t* a) {
|
||||||
try {
|
try {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "start_synthesis calling \n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "start_synthesis calling, text %s\n", text);
|
||||||
auto result = std::strncmp(text, "<speak", 6) == 0 ?
|
auto result = std::strncmp(text, "<speak", 6) == 0 ?
|
||||||
speechSynthesizer->SpeakSsmlAsync(text).get() :
|
speechSynthesizer->SpeakSsmlAsync(text).get() :
|
||||||
speechSynthesizer->SpeakTextAsync(text).get();
|
speechSynthesizer->SpeakTextAsync(text).get();
|
||||||
@@ -43,6 +45,8 @@ static void start_synthesis(std::shared_ptr<SpeechSynthesizer> speechSynthesizer
|
|||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_azure_tts: Exception in start_synthesis %s\n", e.what());
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_azure_tts: Exception in start_synthesis %s\n", e.what());
|
||||||
}
|
}
|
||||||
a->draining = 1;
|
a->draining = 1;
|
||||||
|
|
||||||
|
free((void*) text);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -87,14 +91,7 @@ extern "C" {
|
|||||||
|
|
||||||
switch_status_t azure_speech_feed_tts(azure_t* a, char* text, switch_speech_flag_t *flags) {
|
switch_status_t azure_speech_feed_tts(azure_t* a, char* text, switch_speech_flag_t *flags) {
|
||||||
const int MAX_CHARS = 20;
|
const int MAX_CHARS = 20;
|
||||||
char tempText[MAX_CHARS + 4]; // +4 for the ellipsis and null terminator
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts %s\n", text);
|
||||||
|
|
||||||
if (strlen(text) > MAX_CHARS) {
|
|
||||||
strncpy(tempText, text, MAX_CHARS);
|
|
||||||
strcpy(tempText + MAX_CHARS, "...");
|
|
||||||
} else {
|
|
||||||
strcpy(tempText, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* open cache file */
|
/* open cache file */
|
||||||
if (a->cache_audio && fullDirPath.length() > 0) {
|
if (a->cache_audio && fullDirPath.length() > 0) {
|
||||||
@@ -168,8 +165,14 @@ extern "C" {
|
|||||||
speechConfig->SetEndpointId(a->endpointId);
|
speechConfig->SetEndpointId(a->endpointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (audioLogFile) {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts enabling audio logging to %s\n", audioLogFile);
|
||||||
|
speechConfig->SetProperty(PropertyId::Speech_LogFilename, audioLogFile);
|
||||||
|
speechConfig->EnableAudioLogging();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);
|
auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig, nullptr);
|
||||||
|
|
||||||
speechSynthesizer->SynthesisStarted += [a](const SpeechSynthesisEventArgs& e) {
|
speechSynthesizer->SynthesisStarted += [a](const SpeechSynthesisEventArgs& e) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts SynthesisStarted\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts SynthesisStarted\n");
|
||||||
@@ -266,7 +269,8 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread(start_synthesis, speechSynthesizer, text, a).detach();
|
const char* dupText = strdup(text); // text will be freed in the thread
|
||||||
|
std::thread(start_synthesis, speechSynthesizer, dupText, a).detach();
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts sent synthesize request\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "azure_speech_feed_tts sent synthesize request\n");
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_azure_tts: Exception: %s\n", e.what());
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mod_azure_tts: Exception: %s\n", e.what());
|
||||||
|
|||||||
+5
-3
@@ -159,14 +159,16 @@ static switch_status_t dub_silence_track(switch_core_session_t *session, char* t
|
|||||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||||
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, MY_BUG_NAME);
|
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, MY_BUG_NAME);
|
||||||
switch_status_t status = SWITCH_STATUS_FALSE;
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||||||
|
|
||||||
|
// DH: I found it is much simpler to implement silence as a sequence of remove and add operations
|
||||||
if (bug) {
|
if (bug) {
|
||||||
struct cap_cb *cb =(struct cap_cb *) switch_core_media_bug_get_user_data(bug);
|
struct cap_cb *cb =(struct cap_cb *) switch_core_media_bug_get_user_data(bug);
|
||||||
status = silence_dub_track(cb, trackName);
|
status = remove_dub_track(cb, trackName);
|
||||||
if (status != SWITCH_STATUS_SUCCESS) {
|
if (status != SWITCH_STATUS_SUCCESS) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "dub_silence_track: error silencing track %s\n", trackName);
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "dub_silence_track: error finding track %s\n", trackName);
|
||||||
return SWITCH_STATUS_FALSE;
|
return SWITCH_STATUS_FALSE;
|
||||||
}
|
}
|
||||||
|
status = dub_add_track(session, trackName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "dub_silence_track: bug not found\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "dub_silence_track: bug not found\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user