add openal code to mod_conference

This commit is contained in:
Anthony Minessale 2014-06-30 01:23:03 +05:00
parent 810717cae0
commit 0ac78cacc3
2 changed files with 613 additions and 25 deletions

View File

@ -6,3 +6,9 @@ mod_conference_la_SOURCES = mod_conference.c
mod_conference_la_CFLAGS = $(AM_CFLAGS)
mod_conference_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared
if HAVE_OPENAL
mod_conference_la_LDFLAGS += -lopenal -lm
mod_conference_la_CFLAGS += -DOPENAL_POSITIONING
endif

View File

@ -39,6 +39,14 @@
*
*/
#include <switch.h>
#ifdef OPENAL_POSITIONING
#define AL_ALEXT_PROTOTYPES
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#endif
#define DEFAULT_AGC_LEVEL 1100
#define CONFERENCE_UUID_VARIABLE "conference_uuid"
@ -187,7 +195,9 @@ typedef enum {
MFLAG_PAUSE_RECORDING = (1 << 22),
MFLAG_ACK_VIDEO = (1 << 23),
MFLAG_GHOST = (1 << 24),
MFLAG_JOIN_ONLY = (1 << 25)
MFLAG_JOIN_ONLY = (1 << 25),
MFLAG_POSITIONAL = (1 << 26),
MFLAG_NO_POSITIONAL = (1 << 27)
} member_flag_t;
typedef enum {
@ -213,7 +223,8 @@ typedef enum {
CFLAG_VID_FLOOR_LOCK = (1 << 19),
CFLAG_JSON_EVENTS = (1 << 20),
CFLAG_LIVEARRAY_SYNC = (1 << 21),
CFLAG_CONF_RESTART_AUTO_RECORD = (1 << 22)
CFLAG_CONF_RESTART_AUTO_RECORD = (1 << 22),
CFLAG_POSITIONAL = (1 << 23)
} conf_flag_t;
typedef enum {
@ -262,8 +273,26 @@ typedef enum {
EFLAG_RECORD = (1 << 27),
EFLAG_HUP_MEMBER = (1 << 28),
EFLAG_PLAY_FILE_DONE = (1 << 29),
EFLAG_SET_POSITION_MEMBER = (1 << 30)
} event_type_t;
#ifdef OPENAL_POSITIONING
typedef struct al_handle_s {
ALCdevice *device;
ALCcontext *context;
ALuint source;
ALuint buffer_in[2];
int setpos;
ALfloat pos_x;
ALfloat pos_y;
ALfloat pos_z;
} al_handle_t;
#else
typedef struct al_handle_s {
int unsupported;
} al_handle_t;
#endif
typedef struct conference_file_node {
switch_file_handle_t fh;
switch_speech_handle_t *sh;
@ -277,6 +306,7 @@ typedef struct conference_file_node {
char *file;
switch_bool_t mux;
uint32_t member_id;
al_handle_t *al;
} conference_file_node_t;
typedef enum {
@ -415,6 +445,7 @@ typedef struct conference_obj {
struct vid_helper vh[2];
struct vid_helper mh;
conference_record_t *rec_node_head;
int last_speech_channels;
} conference_obj_t;
/* Relationship with another member */
@ -487,6 +518,8 @@ struct conference_member {
cJSON *json;
cJSON *status_field;
uint8_t loop_loop;
al_handle_t *al;
int last_speech_channels;
};
typedef enum {
@ -582,6 +615,187 @@ static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference
//#define lock_member(_member) switch_mutex_lock(_member->write_mutex)
//#define unlock_member(_member) switch_mutex_unlock(_member->write_mutex)
static al_handle_t *create_al(switch_memory_pool_t *pool)
{
al_handle_t *al;
al = switch_core_alloc(pool, sizeof(al_handle_t));
return al;
}
#ifndef OPENAL_POSITIONING
static void gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream)
{
}
static void process_al(al_handle_t *al, void *data, switch_size_t datalen, int rate)
{
}
#else
static void gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream)
{
float offset;
float pos;
float radius;
float x, z;
float div = 3.14159f / 180;
conference_member_t *member;
if (!conference->count) {
return;
}
switch_mutex_lock(conference->member_mutex);
if (conference->count < 3) {
for (member = conference->members; member; member = member->next) {
if (member->al) {
member->al->pos_x = 0;
member->al->pos_y = 0;
member->al->pos_z = 0;
member->al->setpos = 1;
if (stream) {
stream->write_function(stream, "Member %d (%s) 0.0:0.0:0.0\n", member->id, switch_channel_get_name(member->channel));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member %d (%s) 0.0:0.0:0.0\n",
member->id, switch_channel_get_name(member->channel));
}
}
}
goto end;
}
offset = 180 / (conference->count - 1);
radius = 2.0f;//3.0f; //(float)conference->count / 2.0f; //3.0f;
pos = -90.0f;// + (offset / 2.0f);
for (member = conference->members; member; member = member->next) {
if (switch_test_flag(member, MFLAG_NO_POSITIONAL)) {
continue;
}
if (!member->al) {
member->al = create_al(member->pool);
}
switch_set_flag(member, MFLAG_POSITIONAL);
if (pos == 0) {
x = 0;
z = radius;
} else if (pos == -90) {
z = 0;
x = radius * -1;
} else if (pos == 90) {
z = 0;
x = radius;
} else if (pos < 0) {
z = cos((90+pos) * div) * radius;
x = sin((90+pos) * div) * radius * -1.0f;
} else {
x = cos(pos * div) * radius;
z = sin(pos * div) * radius;
}
member->al->pos_x = x;
member->al->pos_y = 0;
member->al->pos_z = z;
member->al->setpos = 1;
if (stream) {
stream->write_function(stream, "Member %d (%s) %0.2f:0.0:%0.2f\n", member->id, switch_channel_get_name(member->channel), x, z);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member %d (%s) %0.2f:0.0:%0.2f\n",
member->id, switch_channel_get_name(member->channel), x, z);
}
pos += offset;
}
end:
switch_mutex_unlock(conference->member_mutex);
return;
}
#define ALC_HRTF_SOFT 0x1992
static void process_al(al_handle_t *al, void *data, switch_size_t datalen, int rate)
{
if (!al->device) {
ALCint contextAttr[] = {
ALC_FORMAT_CHANNELS_SOFT, ALC_STEREO_SOFT,
ALC_FORMAT_TYPE_SOFT, ALC_SHORT_SOFT,
ALC_FREQUENCY, rate,
ALC_HRTF_SOFT, AL_TRUE,
0
};
if ((al->device = alcLoopbackOpenDeviceSOFT(NULL))) {
static const ALshort silence[16] = { 0 };
//if ((al->device = alcOpenDevice( NULL ))) {
//float orient[6] = { /*fwd:*/ 0., 0., -1., /*up:*/ 0., 1., 0. };
al->context = alcCreateContext(al->device, contextAttr);
alcSetThreadContext(al->context);
/* listener at origin, facing down -z (ears at 1.5m height) */
//alListener3f( AL_POSITION, 0. ,0, 0. );
//alListener3f( AL_VELOCITY, 0., 0., 0. );
//alListenerfv( AL_ORIENTATION, orient );
alGenSources(1, &al->source);
alSourcef( al->source, AL_PITCH, 1.);
alSourcef( al->source, AL_GAIN, 1.);
alGenBuffers(2, al->buffer_in);
alBufferData(al->buffer_in[0], AL_FORMAT_MONO16, data, datalen, rate);
//alBufferData(al->buffer_in[0], AL_FORMAT_MONO16, NULL, 0, rate);
alBufferData(al->buffer_in[1], AL_FORMAT_MONO16, silence, sizeof(silence), rate);
alSourceQueueBuffers(al->source, 2, al->buffer_in);
alSourcePlay(al->source);
}
}
if (al->device) {
ALint processed = 0, state = 0;
alcSetThreadContext(al->context);
alGetSourcei(al->source, AL_SOURCE_STATE, &state);
alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &processed);
if (al->setpos) {
al->setpos = 0;
alSource3f(al->source, AL_POSITION, al->pos_x, al->pos_y, al->pos_z);
}
if (processed > 0) {
ALuint bufid;
alSourceUnqueueBuffers(al->source, 1, &bufid);
alBufferData(bufid, AL_FORMAT_MONO16, data, datalen, rate);
alSourceQueueBuffers(al->source, 1, &bufid);
}
if (state != AL_PLAYING) {
alSourcePlay(al->source);
}
alcRenderSamplesSOFT(al->device, data, datalen / 2);
}
}
#endif
static void conference_cdr_del(conference_member_t *member)
{
@ -707,7 +921,7 @@ static char *conference_rfc4579_render(conference_obj_t *conference, switch_even
if (!(x_tag = switch_xml_add_child_d(xml, "conference-description", off++))) {
abort();
}
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "display-text", off1++))) {
abort();
}
@ -1789,6 +2003,57 @@ static void adv_la(conference_obj_t *conference, conference_member_t *member, sw
}
}
#ifndef OPENAL_POSITIONING
static switch_status_t parse_position(al_handle_t *al, const char *data)
{
return SWITCH_STATUS_FALSE;
}
#else
static switch_status_t parse_position(al_handle_t *al, const char *data)
{
char *args[3];
int num;
char *dup;
dup = strdup((char *)data);
switch_assert(dup);
if ((num = switch_split(dup, ':', args)) != 3) {
return SWITCH_STATUS_FALSE;
}
al->pos_x = atof(args[0]);
al->pos_y = atof(args[1]);
al->pos_z = atof(args[2]);
al->setpos = 1;
switch_safe_free(dup);
return SWITCH_STATUS_SUCCESS;
}
#endif
#ifndef OPENAL_POSITIONING
static switch_status_t member_parse_position(conference_member_t *member, const char *data)
{
return SWITCH_STATUS_FALSE;
}
#else
static switch_status_t member_parse_position(conference_member_t *member, const char *data)
{
switch_status_t status;
lock_member(member);
status = parse_position(member->al, data);
unlock_member(member);
return status;
}
#endif
/* Gain exclusive access and add the member to the list */
static switch_status_t conference_add_member(conference_obj_t *conference, conference_member_t *member)
{
@ -1797,7 +2062,7 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
char msg[512]; /* conference count announcement */
call_list_t *call_list = NULL;
switch_channel_t *channel;
const char *controls = NULL;
const char *controls = NULL, *position = NULL;
switch_assert(conference != NULL);
switch_assert(member != NULL);
@ -1821,6 +2086,7 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
switch_mutex_unlock(conference->member_mutex);
conference_cdr_add(member);
if (!switch_test_flag(member, MFLAG_NOCHANNEL)) {
if (switch_test_flag(member, MFLAG_GHOST)) {
conference->count_ghosts++;
@ -1942,6 +2208,30 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
switch_channel_clear_app_flag_key("conf_silent", channel, CONF_SILENT_REQ);
switch_channel_set_app_flag_key("conf_silent", channel, CONF_SILENT_DONE);
if ((position = switch_channel_get_variable(channel, "conference_position"))) {
if (conference->channels == 2) {
if (switch_test_flag(member, MFLAG_NO_POSITIONAL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"%s has positional audio blocked.\n", switch_channel_get_name(channel));
} else {
if (member_parse_position(member, position) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s invalid position data\n", switch_channel_get_name(channel));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s position data set\n", switch_channel_get_name(channel));
}
switch_set_flag(member, MFLAG_POSITIONAL);
member->al = create_al(member->pool);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s cannot set position data on mono conference.\n", switch_channel_get_name(channel));
}
}
controls = switch_channel_get_variable(channel, "conference_controls");
if (zstr(controls)) {
@ -1988,6 +2278,11 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
}
if (switch_test_flag(conference, CFLAG_POSITIONAL)) {
gen_arc(conference, NULL);
}
send_rfc_event(conference);
send_json_event(conference);
@ -2155,6 +2450,17 @@ static void conference_set_floor_holder(conference_obj_t *conference, conference
}
#ifdef OPENAL_POSITIONING
static void close_al(al_handle_t *al)
{
alDeleteSources(1, &al->source);
alDeleteBuffers(2, al->buffer_in);
alcDestroyContext(al->context);
alcCloseDevice(al->device);
al->device = NULL;
}
#endif
static switch_status_t conference_file_close(conference_obj_t *conference, conference_file_node_t *node)
{
switch_event_t *event;
@ -2193,7 +2499,13 @@ static switch_status_t conference_file_close(conference_obj_t *conference, confe
switch_event_fire(&event);
}
#ifdef OPENAL_POSITIONING
if (node->al && node->al->device) {
close_al(node->al);
}
#endif
return switch_core_file_close(&node->fh);
}
@ -2223,6 +2535,11 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
conference_cdr_del(member);
#ifdef OPENAL_POSITIONING
if (member->al && member->al->device) {
close_al(member->al);
}
#endif
member_fnode = member->fnode;
member_sh = member->sh;
@ -2344,6 +2661,10 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
send_rfc_event(conference);
send_json_event(conference);
if (switch_test_flag(conference, CFLAG_POSITIONAL)) {
gen_arc(conference, NULL);
}
switch_mutex_unlock(conference->mutex);
status = SWITCH_STATUS_SUCCESS;
@ -2748,9 +3069,20 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
if (conference->fnode->type == NODE_TYPE_SPEECH) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING;
switch_size_t speech_len = file_data_len;
if (switch_core_speech_read_tts(conference->fnode->sh, file_frame, &file_data_len, &flags) == SWITCH_STATUS_SUCCESS) {
if (conference->fnode->al) {
speech_len /= 2;
}
if (switch_core_speech_read_tts(conference->fnode->sh, file_frame, &speech_len, &flags) == SWITCH_STATUS_SUCCESS) {
if (conference->fnode->al) {
process_al(conference->fnode->al, file_frame, speech_len, conference->rate);
}
file_sample_len = file_data_len / 2 / conference->fnode->sh->channels;
} else {
file_sample_len = file_data_len = 0;
@ -2760,6 +3092,9 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
if (conference->fnode->fh.vol) {
switch_change_sln_volume_granular((void *)file_frame, (uint32_t)file_sample_len, conference->fnode->fh.vol);
}
if (conference->fnode->al) {
process_al(conference->fnode->al, file_frame, file_sample_len * 2, conference->fnode->fh.samplerate);
}
}
if (file_sample_len <= 0) {
@ -2777,7 +3112,9 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
} else if (!conference->async_fnode->done) {
file_sample_len = samples;
switch_core_file_read(&conference->async_fnode->fh, async_file_frame, &file_sample_len);
if (conference->async_fnode->al) {
process_al(conference->async_fnode->al, file_frame, file_sample_len * 2, conference->async_fnode->fh.samplerate);
}
if (file_sample_len <= 0) {
conference->async_fnode->done++;
} else {
@ -3683,13 +4020,12 @@ static void check_agc_levels(conference_member_t *member)
}
}
static void member_check_channels(switch_frame_t *frame, conference_member_t *member, switch_bool_t in)
{
if (member->conference->channels != member->read_impl.number_of_channels) {
if (member->conference->channels != member->read_impl.number_of_channels || switch_test_flag(member, MFLAG_POSITIONAL)) {
uint32_t rlen;
int from, to;
if (in) {
to = member->conference->channels;
from = member->read_impl.number_of_channels;
@ -3699,10 +4035,21 @@ static void member_check_channels(switch_frame_t *frame, conference_member_t *me
}
rlen = frame->datalen / 2 / from;
switch_mux_channels((int16_t *) frame->data, rlen, from, to);
if (((from == 1 && to == 2) || (from == 2 && to == 2 && in)) && switch_test_flag(member, MFLAG_POSITIONAL)) {
if (from == 2 && to == 2) {
switch_mux_channels((int16_t *) frame->data, rlen, 2, 1);
frame->datalen /= 2;
rlen = frame->datalen / 2;
}
process_al(member->al, frame->data, frame->datalen, member->read_impl.actual_samples_per_second);
} else {
switch_mux_channels((int16_t *) frame->data, rlen, from, to);
}
frame->datalen = rlen * 2 * to;
}
}
@ -4102,9 +4449,14 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi
} else {
if (member->fnode->type == NODE_TYPE_SPEECH) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING;
switch_size_t speech_len = file_data_len;
if (member->fnode->al) {
speech_len /= 2;
}
if (switch_core_speech_read_tts(member->fnode->sh, file_frame, &file_data_len, &flags) == SWITCH_STATUS_SUCCESS) {
file_sample_len = file_data_len / 2 / member->conference->channels;
if (switch_core_speech_read_tts(member->fnode->sh, file_frame, &speech_len, &flags) == SWITCH_STATUS_SUCCESS) {
file_sample_len = file_data_len / 2 / member->conference->channels;
} else {
file_sample_len = file_data_len = 0;
}
@ -4123,6 +4475,10 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi
switch_change_sln_volume(file_frame, (uint32_t)file_sample_len, member->volume_out_level);
}
if (member->fnode->al) {
process_al(member->fnode->al, file_frame, file_sample_len * 2, member->conference->rate);
}
for (i = 0; i < (int)file_sample_len * member->conference->channels; i++) {
if (member->fnode->mux) {
sample = data[i] + file_frame[i];
@ -4886,6 +5242,8 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char *
uint32_t count;
char *dfile = NULL, *expanded = NULL;
int say = 0;
uint8_t channels = (uint8_t) conference->channels;
int bad_params = 0;
switch_assert(conference != NULL);
@ -4952,9 +5310,16 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char *
fnode->type = NODE_TYPE_FILE;
fnode->leadin = leadin;
if (switch_stristr("position=", file)) {
/* positional requires mono input */
fnode->fh.channels = channels = 1;
}
retry:
/* Open the file */
fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
if (switch_core_file_open(&fnode->fh, file, (uint8_t) conference->channels, conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) !=
if (switch_core_file_open(&fnode->fh, file, channels, conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) !=
SWITCH_STATUS_SUCCESS) {
switch_event_t *event;
@ -4980,10 +5345,23 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char *
if (fnode->fh.params) {
const char *vol = switch_event_get_header(fnode->fh.params, "vol");
const char *position = switch_event_get_header(fnode->fh.params, "position");
if (!zstr(vol)) {
fnode->fh.vol = atoi(vol);
}
if (!bad_params && !zstr(position) && conference->channels == 2) {
fnode->al = create_al(pool);
if (parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
switch_core_file_close(&fnode->fh);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
fnode->al = NULL;
channels = conference->channels;
bad_params = 1;
goto retry;
}
}
}
fnode->pool = pool;
@ -5033,6 +5411,8 @@ static switch_status_t conference_member_play_file(conference_member_t *member,
char *dfile = NULL, *expanded = NULL;
conference_file_node_t *fnode, *nptr = NULL;
switch_memory_pool_t *pool;
int channels = member->conference->channels;
int bad_params = 0;
if (member == NULL || file == NULL || switch_test_flag(member, MFLAG_KICKED))
return status;
@ -5077,10 +5457,17 @@ static switch_status_t conference_member_play_file(conference_member_t *member,
fnode->mux = mux;
fnode->member_id = member->id;
if (switch_stristr("position=", file)) {
/* positional requires mono input */
fnode->fh.channels = channels = 1;
}
retry:
/* Open the file */
fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
if (switch_core_file_open(&fnode->fh,
file, (uint8_t) member->conference->channels, member->conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
file, (uint8_t) channels, member->conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
pool) != SWITCH_STATUS_SUCCESS) {
switch_core_destroy_memory_pool(&pool);
status = SWITCH_STATUS_NOTFOUND;
@ -5088,6 +5475,23 @@ static switch_status_t conference_member_play_file(conference_member_t *member,
}
fnode->pool = pool;
fnode->file = switch_core_strdup(fnode->pool, file);
if (fnode->fh.params) {
const char *position = switch_event_get_header(fnode->fh.params, "position");
if (!bad_params && !zstr(position) && member->conference->channels == 2) {
fnode->al = create_al(pool);
if (parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
switch_core_file_close(&fnode->fh);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Invalid Position Data.\n");
fnode->al = NULL;
channels = member->conference->channels;
bad_params = 1;
goto retry;
}
}
}
/* Queue the node */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Queueing file '%s' for play\n", file);
switch_mutex_lock(member->fnode_mutex);
@ -5116,6 +5520,10 @@ static switch_status_t conference_member_say(conference_member_t *member, char *
switch_memory_pool_t *pool;
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_status_t status = SWITCH_STATUS_FALSE;
char *fp = NULL;
int channels = member->conference->channels;
switch_event_t *params = NULL;
const char *position = NULL;
if (member == NULL || zstr(text))
return SWITCH_STATUS_FALSE;
@ -5139,18 +5547,55 @@ static switch_status_t conference_member_say(conference_member_t *member, char *
return SWITCH_STATUS_MEMERR;
}
if (*text == '{') {
char *new_fp;
fp = switch_core_strdup(pool, text);
switch_assert(fp);
if (!switch_event_create_brackets(fp, '{', '}', ',', &params, &new_fp, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
new_fp = fp;
}
text = new_fp;
}
fnode->type = NODE_TYPE_SPEECH;
fnode->leadin = leadin;
fnode->pool = pool;
if (params && (position = switch_event_get_header(params, "position"))) {
if (conference->channels != 2) {
position = NULL;
} else {
channels = 1;
fnode->al = create_al(pool);
if (parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
fnode->al = NULL;
channels = conference->channels;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
}
}
}
if (member->sh && member->last_speech_channels != channels) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_core_speech_close(&member->lsh, &flags);
member->sh = NULL;
}
if (!member->sh) {
memset(&member->lsh, 0, sizeof(member->lsh));
if (switch_core_speech_open(&member->lsh, conference->tts_engine, conference->tts_voice,
conference->rate, conference->interval, conference->channels, &flags, switch_core_session_get_pool(member->session)) !=
conference->rate, conference->interval, channels, &flags, switch_core_session_get_pool(member->session)) !=
SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine);
return SWITCH_STATUS_FALSE;
status = SWITCH_STATUS_FALSE;
goto end;
}
member->last_speech_channels = channels;
member->sh = &member->lsh;
}
@ -5185,6 +5630,12 @@ static switch_status_t conference_member_say(conference_member_t *member, char *
status = SWITCH_STATUS_SUCCESS;
end:
if (params) {
switch_event_destroy(&params);
}
return status;
}
@ -5196,13 +5647,20 @@ static switch_status_t conference_say(conference_obj_t *conference, const char *
switch_memory_pool_t *pool;
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
uint32_t count;
switch_event_t *params = NULL;
char *fp = NULL;
int channels;
const char *position = NULL;
switch_assert(conference != NULL);
channels = conference->channels;
if (zstr(text)) {
return SWITCH_STATUS_GENERR;
}
switch_mutex_lock(conference->mutex);
switch_mutex_lock(conference->member_mutex);
count = conference->count;
@ -5229,16 +5687,53 @@ static switch_status_t conference_say(conference_obj_t *conference, const char *
return SWITCH_STATUS_MEMERR;
}
if (*text == '{') {
char *new_fp;
fp = switch_core_strdup(pool, text);
switch_assert(fp);
if (!switch_event_create_brackets(fp, '{', '}', ',', &params, &new_fp, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
new_fp = fp;
}
text = new_fp;
}
fnode->type = NODE_TYPE_SPEECH;
fnode->leadin = leadin;
if (params && (position = switch_event_get_header(params, "position"))) {
if (conference->channels != 2) {
position = NULL;
} else {
channels = 1;
fnode->al = create_al(pool);
if (parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
fnode->al = NULL;
channels = conference->channels;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
}
}
}
if (conference->sh && conference->last_speech_channels != channels) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_core_speech_close(&conference->lsh, &flags);
conference->sh = NULL;
}
if (!conference->sh) {
memset(&conference->lsh, 0, sizeof(conference->lsh));
if (switch_core_speech_open(&conference->lsh, conference->tts_engine, conference->tts_voice,
conference->rate, conference->interval, conference->channels, &flags, NULL) != SWITCH_STATUS_SUCCESS) {
conference->rate, conference->interval, channels, &flags, NULL) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine);
return SWITCH_STATUS_FALSE;
status = SWITCH_STATUS_FALSE;
goto end;
}
conference->last_speech_channels = channels;
conference->sh = &conference->lsh;
}
@ -5273,6 +5768,12 @@ static switch_status_t conference_say(conference_obj_t *conference, const char *
switch_mutex_unlock(conference->mutex);
status = SWITCH_STATUS_SUCCESS;
end:
if (params) {
switch_event_destroy(&params);
}
return status;
}
@ -5721,8 +6222,9 @@ static switch_status_t conf_api_sub_energy(conference_member_t *member, switch_s
{
switch_event_t *event;
if (member == NULL)
if (member == NULL) {
return SWITCH_STATUS_GENERR;
}
if (data) {
lock_member(member);
@ -5743,6 +6245,79 @@ static switch_status_t conf_api_sub_energy(conference_member_t *member, switch_s
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t conf_api_sub_auto_position(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
{
#ifdef OPENAL_POSITIONING
gen_arc(conference, stream);
stream->write_function(stream, "+OK\n");
#else
stream->write_function(stream, "-ERR not supported\n");
#endif
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t conf_api_sub_position(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{
#ifndef OPENAL_POSITIONING
stream->write_function(stream, "-ERR not supported\n");
#else
switch_event_t *event;
if (member == NULL) {
return SWITCH_STATUS_GENERR;
}
if (switch_test_flag(member, MFLAG_NO_POSITIONAL)) {
if (stream) stream->write_function(stream,
"%s has positional audio blocked.\n", switch_channel_get_name(member->channel));
return SWITCH_STATUS_SUCCESS;
}
if (!member->al) {
if (!switch_test_flag(member, MFLAG_POSITIONAL) && member->conference->channels == 2) {
switch_set_flag(member, MFLAG_POSITIONAL);
member->al = create_al(member->pool);
} else {
if (stream) {
stream->write_function(stream, "Positional audio not avalilable %d\n", member->conference->channels);
}
return SWITCH_STATUS_FALSE;
}
}
if (data) {
if (member_parse_position(member, data) != SWITCH_STATUS_SUCCESS) {
if (stream) {
stream->write_function(stream, "invalid input!\n");
}
return SWITCH_STATUS_FALSE;
}
}
if (stream != NULL) {
stream->write_function(stream, "Position %u = %0.2f:%0.2f:%0.2f\n", member->id, member->al->pos_x, member->al->pos_y, member->al->pos_z);
}
if (test_eflag(member->conference, EFLAG_SET_POSITION_MEMBER) &&
data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_member_data(member, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "set-position-member");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Position", "%0.2f:%0.2f:%0.2f", member->al->pos_x, member->al->pos_y, member->al->pos_z);
switch_event_fire(&event);
}
#endif
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t conf_api_sub_volume_in(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{
switch_event_t *event;
@ -7303,6 +7878,8 @@ static api_command_t conf_api_sub_commands[] = {
{"energy", (void_fn_t) & conf_api_sub_energy, CONF_API_SUB_MEMBER_TARGET, "energy", "<member_id|all|last|non_moderator> [<newval>]"},
{"volume_in", (void_fn_t) & conf_api_sub_volume_in, CONF_API_SUB_MEMBER_TARGET, "volume_in", "<member_id|all|last|non_moderator> [<newval>]"},
{"volume_out", (void_fn_t) & conf_api_sub_volume_out, CONF_API_SUB_MEMBER_TARGET, "volume_out", "<member_id|all|last|non_moderator> [<newval>]"},
{"position", (void_fn_t) & conf_api_sub_position, CONF_API_SUB_MEMBER_TARGET, "position", "<member_id> <x>,<y>,<z>"},
{"auto-3d-position", (void_fn_t) & conf_api_sub_auto_position, CONF_API_SUB_ARGS_SPLIT, "auto-3d-position", ""},
{"play", (void_fn_t) & conf_api_sub_play, CONF_API_SUB_ARGS_SPLIT, "play", "<file_path> [async|<member_id> [nomux]]"},
{"pause_play", (void_fn_t) & conf_api_sub_pause_play, CONF_API_SUB_ARGS_SPLIT, "pause", ""},
{"file_seek", (void_fn_t) & conf_api_sub_file_seek, CONF_API_SUB_ARGS_SPLIT, "file_seek", "[+-]<val>"},
@ -7931,6 +8508,10 @@ static void set_mflags(const char *flags, member_flag_t *f)
*f |= MFLAG_GHOST;
} else if (!strcasecmp(argv[i], "join-only")) {
*f |= MFLAG_JOIN_ONLY;
} else if (!strcasecmp(argv[i], "positional")) {
*f |= MFLAG_POSITIONAL;
} else if (!strcasecmp(argv[i], "no-positional")) {
*f |= MFLAG_NO_POSITIONAL;
}
}
@ -7973,6 +8554,8 @@ static void set_cflags(const char *flags, uint32_t *f)
*f |= CFLAG_LIVEARRAY_SYNC;
} else if (!strcasecmp(argv[i], "rfc-4579")) {
*f |= CFLAG_RFC4579;
} else if (!strcasecmp(argv[i], "auto-3d-position")) {
*f |= CFLAG_POSITIONAL;
}
@ -9108,7 +9691,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c
} else {
tmp = atoi(force_rate);
if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 48000) {
if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 44100 || tmp == 48000) {
force_rate_i = rate = tmp;
}
}
@ -9166,7 +9749,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c
rate = read_impl.actual_samples_per_second;
}
} else {
if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 48000) {
if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 44100 || tmp == 48000) {
rate = tmp;
}
}
@ -10164,7 +10747,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load)
SWITCH_ADD_APP(app_interface, global_app_name, global_app_name, NULL, conference_function, NULL, SAF_NONE);
SWITCH_ADD_APP(app_interface, "conference_set_auto_outcall", "conference_set_auto_outcall", NULL, conference_auto_function, NULL, SAF_NONE);
SWITCH_ADD_CHAT(chat_interface, CONF_CHAT_PROTO, chat_send);
send_presence(SWITCH_EVENT_PRESENCE_IN);