add new channel option (via ast_channel_setoption()) to let channel drivers adjust txgain/rxgain if they are able (only Zap channels at this time)
modify app_chanspy to use new gain option reformat app_chanspy to match coding guidelines add user-controlled volume adjustment to app_meetme (issue #4170, heavily modified to actually work on Zap channels) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6519 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
2f67f66577
commit
5fdc070109
|
@ -39,12 +39,10 @@ AST_MUTEX_DEFINE_STATIC(modlock);
|
|||
#define AST_NAME_STRLEN 256
|
||||
#define ALL_DONE(u, ret) LOCAL_USER_REMOVE(u); return ret;
|
||||
#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
|
||||
#define minmax(x,y) x ? (x > y) ? y : ((x < (y * -1)) ? (y * -1) : x) : 0
|
||||
|
||||
|
||||
static char *synopsis = "Tap into any type of asterisk channel and listen to audio";
|
||||
static char *app = "ChanSpy";
|
||||
static char *desc = " Chanspy([<scanspec>][|<options>])\n\n"
|
||||
static const char *synopsis = "Tap into any type of asterisk channel and listen to audio";
|
||||
static const char *app = "ChanSpy";
|
||||
static const char *desc = " Chanspy([<scanspec>][|<options>])\n\n"
|
||||
"Valid Options:\n"
|
||||
" - q: quiet, don't announce channels beep, etc.\n"
|
||||
" - b: bridged, only spy on channels involved in a bridged call.\n"
|
||||
|
@ -237,104 +235,102 @@ static int spy_queue_ready(struct ast_channel_spy *spy)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
|
||||
struct chanspy_translation_helper *csth = data;
|
||||
struct ast_frame frame, *f;
|
||||
int len0 = 0, len1 = 0, samp0 = 0, samp1 = 0, x, vf, maxsamp;
|
||||
short buf0[1280], buf1[1280], buf[1280];
|
||||
struct chanspy_translation_helper *csth = data;
|
||||
struct ast_frame frame, *f;
|
||||
int len0 = 0, len1 = 0, samp0 = 0, samp1 = 0, x, vf, maxsamp;
|
||||
short buf0[1280], buf1[1280], buf[1280];
|
||||
|
||||
if (csth->spy.status == CHANSPY_DONE) {
|
||||
return -1;
|
||||
if (csth->spy.status == CHANSPY_DONE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&csth->spy.lock);
|
||||
while((f = csth->spy.queue[0])) {
|
||||
csth->spy.queue[0] = f->next;
|
||||
ast_slinfactory_feed(&csth->slinfactory[0], f);
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_mutex_unlock(&csth->spy.lock);
|
||||
ast_mutex_lock(&csth->spy.lock);
|
||||
while((f = csth->spy.queue[1])) {
|
||||
csth->spy.queue[1] = f->next;
|
||||
ast_slinfactory_feed(&csth->slinfactory[1], f);
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_mutex_unlock(&csth->spy.lock);
|
||||
ast_mutex_lock(&csth->spy.lock);
|
||||
while((f = csth->spy.queue[0])) {
|
||||
csth->spy.queue[0] = f->next;
|
||||
ast_slinfactory_feed(&csth->slinfactory[0], f);
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_mutex_unlock(&csth->spy.lock);
|
||||
ast_mutex_lock(&csth->spy.lock);
|
||||
while((f = csth->spy.queue[1])) {
|
||||
csth->spy.queue[1] = f->next;
|
||||
ast_slinfactory_feed(&csth->slinfactory[1], f);
|
||||
ast_frfree(f);
|
||||
}
|
||||
ast_mutex_unlock(&csth->spy.lock);
|
||||
|
||||
if (csth->slinfactory[0].size < len || csth->slinfactory[1].size < len) {
|
||||
return 0;
|
||||
}
|
||||
if (csth->slinfactory[0].size < len || csth->slinfactory[1].size < len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((len0 = ast_slinfactory_read(&csth->slinfactory[0], buf0, len))) {
|
||||
samp0 = len0 / 2;
|
||||
}
|
||||
if((len1 = ast_slinfactory_read(&csth->slinfactory[1], buf1, len))) {
|
||||
samp1 = len1 / 2;
|
||||
}
|
||||
if ((len0 = ast_slinfactory_read(&csth->slinfactory[0], buf0, len))) {
|
||||
samp0 = len0 / 2;
|
||||
}
|
||||
if ((len1 = ast_slinfactory_read(&csth->slinfactory[1], buf1, len))) {
|
||||
samp1 = len1 / 2;
|
||||
}
|
||||
|
||||
maxsamp = (samp0 > samp1) ? samp0 : samp1;
|
||||
vf = get_volfactor(csth->volfactor);
|
||||
vf = minmax(vf, 16);
|
||||
maxsamp = (samp0 > samp1) ? samp0 : samp1;
|
||||
vf = get_volfactor(csth->volfactor);
|
||||
|
||||
for(x=0; x < maxsamp; x++) {
|
||||
if (vf < 0) {
|
||||
if (samp0) {
|
||||
buf0[x] /= abs(vf);
|
||||
}
|
||||
if (samp1) {
|
||||
buf1[x] /= abs(vf);
|
||||
}
|
||||
} else if (vf > 0) {
|
||||
if (samp0) {
|
||||
buf0[x] *= vf;
|
||||
}
|
||||
if (samp1) {
|
||||
buf1[x] *= vf;
|
||||
}
|
||||
for(x=0; x < maxsamp; x++) {
|
||||
if (vf < 0) {
|
||||
if (samp0) {
|
||||
buf0[x] /= abs(vf);
|
||||
}
|
||||
if (samp0 && samp1) {
|
||||
if (x < samp0 && x < samp1) {
|
||||
buf[x] = buf0[x] + buf1[x];
|
||||
} else if (x < samp0) {
|
||||
buf[x] = buf0[x];
|
||||
} else if (x < samp1) {
|
||||
buf[x] = buf1[x];
|
||||
}
|
||||
if (samp1) {
|
||||
buf1[x] /= abs(vf);
|
||||
}
|
||||
} else if (vf > 0) {
|
||||
if (samp0) {
|
||||
buf0[x] *= vf;
|
||||
}
|
||||
if (samp1) {
|
||||
buf1[x] *= vf;
|
||||
}
|
||||
}
|
||||
if (samp0 && samp1) {
|
||||
if (x < samp0 && x < samp1) {
|
||||
buf[x] = buf0[x] + buf1[x];
|
||||
} else if (x < samp0) {
|
||||
buf[x] = buf0[x];
|
||||
} else if (x < samp1) {
|
||||
buf[x] = buf1[x];
|
||||
}
|
||||
} else if (x < samp0) {
|
||||
buf[x] = buf0[x];
|
||||
} else if (x < samp1) {
|
||||
buf[x] = buf1[x];
|
||||
}
|
||||
}
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.frametype = AST_FRAME_VOICE;
|
||||
frame.subclass = AST_FORMAT_SLINEAR;
|
||||
frame.data = buf;
|
||||
frame.samples = x;
|
||||
frame.datalen = x * 2;
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.frametype = AST_FRAME_VOICE;
|
||||
frame.subclass = AST_FORMAT_SLINEAR;
|
||||
frame.data = buf;
|
||||
frame.samples = x;
|
||||
frame.datalen = x * 2;
|
||||
|
||||
if (ast_write(chan, &frame)) {
|
||||
csth->spy.status = CHANSPY_DONE;
|
||||
return -1;
|
||||
}
|
||||
if (ast_write(chan, &frame)) {
|
||||
csth->spy.status = CHANSPY_DONE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (csth->fd) {
|
||||
write(csth->fd, buf1, len1);
|
||||
}
|
||||
if (csth->fd) {
|
||||
write(csth->fd, buf1, len1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct ast_generator spygen = {
|
||||
alloc: spy_alloc,
|
||||
release: spy_release,
|
||||
generate: spy_generate,
|
||||
alloc: spy_alloc,
|
||||
release: spy_release,
|
||||
generate: spy_generate,
|
||||
};
|
||||
|
||||
static void start_spying(struct ast_channel *chan, struct ast_channel *spychan, struct ast_channel_spy *spy)
|
||||
|
@ -398,6 +394,34 @@ static void stop_spying(struct ast_channel *chan, struct ast_channel_spy *spy)
|
|||
|
||||
}
|
||||
|
||||
/* Map 'volume' levels from -4 through +4 into
|
||||
decibel (dB) settings for channel drivers
|
||||
*/
|
||||
static signed char volfactor_map[] = {
|
||||
-24,
|
||||
-18,
|
||||
-12,
|
||||
-6,
|
||||
0,
|
||||
6,
|
||||
12,
|
||||
18,
|
||||
24,
|
||||
};
|
||||
|
||||
/* attempt to set the desired gain adjustment via the channel driver;
|
||||
if successful, clear it out of the csth structure so the
|
||||
generator will not attempt to do the adjustment itself
|
||||
*/
|
||||
static void set_volume(struct ast_channel *chan, struct chanspy_translation_helper *csth)
|
||||
{
|
||||
signed char volume_adjust = volfactor_map[csth->volfactor + 4];
|
||||
|
||||
if (!ast_channel_setoption(chan, AST_OPTION_TXGAIN, &volume_adjust, sizeof(volume_adjust), 0)) {
|
||||
csth->volfactor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd)
|
||||
{
|
||||
struct chanspy_translation_helper csth;
|
||||
|
@ -416,6 +440,7 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
|
|||
csth.spy.status = CHANSPY_RUNNING;
|
||||
ast_mutex_init(&csth.spy.lock);
|
||||
csth.volfactor = *volfactor;
|
||||
set_volume(chan, &csth);
|
||||
|
||||
if (fd) {
|
||||
csth.fd = fd;
|
||||
|
@ -423,12 +448,12 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
|
|||
start_spying(spyee, chan, &csth.spy);
|
||||
ast_activate_generator(chan, &spygen, &csth);
|
||||
|
||||
while(csth.spy.status == CHANSPY_RUNNING &&
|
||||
chan && !ast_check_hangup(chan) &&
|
||||
spyee &&
|
||||
!ast_check_hangup(spyee)
|
||||
&& running == 1 &&
|
||||
(res = ast_waitfor(chan, -1) > -1)) {
|
||||
while (csth.spy.status == CHANSPY_RUNNING &&
|
||||
chan && !ast_check_hangup(chan) &&
|
||||
spyee &&
|
||||
!ast_check_hangup(spyee) &&
|
||||
running == 1 &&
|
||||
(res = ast_waitfor(chan, -1) > -1)) {
|
||||
if ((f = ast_read(chan))) {
|
||||
res = 0;
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
|
@ -456,14 +481,15 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
|
|||
running = x ? atoi(inp) : -1;
|
||||
break;
|
||||
} else {
|
||||
csth.volfactor++;
|
||||
if (csth.volfactor > 4) {
|
||||
csth.volfactor = -4;
|
||||
(*volfactor)++;
|
||||
if (*volfactor > 4) {
|
||||
*volfactor = -4;
|
||||
}
|
||||
if (option_verbose > 2) {
|
||||
ast_verbose(VERBOSE_PREFIX_3"Setting spy volume on %s to %d\n", chan->name, csth.volfactor);
|
||||
ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
|
||||
}
|
||||
*volfactor = csth.volfactor;
|
||||
csth.volfactor = *volfactor;
|
||||
set_volume(chan, &csth);
|
||||
}
|
||||
} else if (res >= 48 && res <= 57) {
|
||||
inp[x++] = res;
|
||||
|
@ -483,8 +509,6 @@ static int channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int
|
|||
return running;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int chanspy_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct localuser *u;
|
||||
|
@ -560,12 +584,13 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
|
|||
silent = ast_test_flag(&flags, OPTION_QUIET);
|
||||
bronly = ast_test_flag(&flags, OPTION_BRIDGED);
|
||||
if (ast_test_flag(&flags, OPTION_VOLUME) && opts[1]) {
|
||||
if (sscanf(opts[0], "%d", &volfactor) != 1)
|
||||
ast_log(LOG_NOTICE, "volfactor must be a number between -4 and 4\n");
|
||||
else {
|
||||
volfactor = minmax(volfactor, 4);
|
||||
int vol;
|
||||
|
||||
if ((sscanf(opts[0], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
|
||||
ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
|
||||
else
|
||||
volfactor = vol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recbase) {
|
||||
|
@ -614,9 +639,9 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
|
|||
}
|
||||
|
||||
if (igrp && (!spec || ((strlen(spec) < strlen(peer->name) &&
|
||||
!strncasecmp(peer->name, spec, strlen(spec)))))) {
|
||||
!strncasecmp(peer->name, spec, strlen(spec)))))) {
|
||||
if (peer && (!bronly || ast_bridged_channel(peer)) &&
|
||||
!ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
|
||||
!ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
|
||||
int x = 0;
|
||||
|
||||
strncpy(peer_name, peer->name, AST_NAME_STRLEN);
|
||||
|
@ -694,7 +719,7 @@ int load_module(void)
|
|||
|
||||
char *description(void)
|
||||
{
|
||||
return synopsis;
|
||||
return (char *) synopsis;
|
||||
}
|
||||
|
||||
int usecount(void)
|
||||
|
|
|
@ -136,16 +136,19 @@ static struct ast_conference {
|
|||
} *confs;
|
||||
|
||||
struct ast_conf_user {
|
||||
int user_no; /* User Number */
|
||||
struct ast_conf_user *prevuser; /* Pointer to the previous user */
|
||||
struct ast_conf_user *nextuser; /* Pointer to the next user */
|
||||
int userflags; /* Flags as set in the conference */
|
||||
int adminflags; /* Flags set by the Admin */
|
||||
struct ast_channel *chan; /* Connected channel */
|
||||
int talking; /* Is user talking */
|
||||
char usrvalue[50]; /* Custom User Value */
|
||||
char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
|
||||
time_t jointime; /* Time the user joined the conference */
|
||||
int user_no; /* User Number */
|
||||
struct ast_conf_user *prevuser; /* Pointer to the previous user */
|
||||
struct ast_conf_user *nextuser; /* Pointer to the next user */
|
||||
int userflags; /* Flags as set in the conference */
|
||||
int adminflags; /* Flags set by the Admin */
|
||||
struct ast_channel *chan; /* Connected channel */
|
||||
int talking; /* Is user talking */
|
||||
int zapchannel; /* Is a Zaptel channel */
|
||||
char usrvalue[50]; /* Custom User Value */
|
||||
char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
|
||||
time_t jointime; /* Time the user joined the conference */
|
||||
int desired_volume; /* Desired volume adjustment */
|
||||
int actual_volume; /* Actual volume adjustment (for channels that can't adjust) */
|
||||
};
|
||||
|
||||
#define ADMINFLAG_MUTED (1 << 1) /* User is muted */
|
||||
|
@ -153,6 +156,11 @@ struct ast_conf_user {
|
|||
#define MEETME_DELAYDETECTTALK 300
|
||||
#define MEETME_DELAYDETECTENDTALK 1000
|
||||
|
||||
enum volume_action {
|
||||
VOL_UP,
|
||||
VOL_DOWN,
|
||||
};
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(conflock);
|
||||
|
||||
static int admin_exec(struct ast_channel *chan, void *data);
|
||||
|
@ -251,6 +259,94 @@ static int careful_write(int fd, unsigned char *data, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Map 'volume' levels from -5 through +5 into
|
||||
decibel (dB) settings for channel drivers
|
||||
Note: these are not a straight linear-to-dB
|
||||
conversion... the numbers have been modified
|
||||
to give the user a better level of adjustability
|
||||
*/
|
||||
static signed char gain_map[] = {
|
||||
-15,
|
||||
-13,
|
||||
-10,
|
||||
-6,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
6,
|
||||
10,
|
||||
13,
|
||||
15,
|
||||
};
|
||||
|
||||
static int set_volume(struct ast_conf_user *user, int volume)
|
||||
{
|
||||
signed char gain_adjust;
|
||||
|
||||
/* attempt to make the adjustment in the channel driver;
|
||||
if successful, don't adjust in the frame reading routine
|
||||
*/
|
||||
gain_adjust = gain_map[volume + 5];
|
||||
return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
|
||||
}
|
||||
|
||||
static void tweak_volume(struct ast_conf_user *user, enum volume_action action)
|
||||
{
|
||||
switch (action) {
|
||||
case VOL_UP:
|
||||
switch (user->desired_volume) {
|
||||
case 5:
|
||||
break;
|
||||
case 0:
|
||||
user->desired_volume = 2;
|
||||
break;
|
||||
case -2:
|
||||
user->desired_volume = 0;
|
||||
break;
|
||||
default:
|
||||
user->desired_volume++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VOL_DOWN:
|
||||
switch (user->desired_volume) {
|
||||
case -5:
|
||||
break;
|
||||
case 2:
|
||||
user->desired_volume = 0;
|
||||
break;
|
||||
case 0:
|
||||
user->desired_volume = -2;
|
||||
break;
|
||||
default:
|
||||
user->desired_volume--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* attempt to make the adjustment in the channel driver;
|
||||
if successful, don't adjust in the frame reading routine
|
||||
*/
|
||||
if (!set_volume(user, user->desired_volume))
|
||||
user->actual_volume = 0;
|
||||
else
|
||||
user->actual_volume = user->desired_volume;
|
||||
}
|
||||
|
||||
static void adjust_volume(struct ast_frame *f, int vol)
|
||||
{
|
||||
int count;
|
||||
short *fdata = f->data;
|
||||
|
||||
for (count = 0; count < f->datalen; count++) {
|
||||
if (vol > 0) {
|
||||
fdata[count] *= abs(vol);
|
||||
} else if (vol < 0) {
|
||||
fdata[count] /= abs(vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
|
||||
{
|
||||
unsigned char *data;
|
||||
|
@ -789,6 +885,7 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c
|
|||
}
|
||||
ast_indicate(chan, -1);
|
||||
retryzap = strcasecmp(chan->type, "Zap");
|
||||
user->zapchannel = retryzap;
|
||||
zapretry:
|
||||
origfd = chan->fds[0];
|
||||
if (retryzap) {
|
||||
|
@ -903,7 +1000,7 @@ zapretry:
|
|||
if (!agifile)
|
||||
agifile = agifiledefault;
|
||||
|
||||
if (!strcasecmp(chan->type,"Zap")) {
|
||||
if (user->zapchannel) {
|
||||
/* Set CONFMUTE mode on Zap channel to mute DTMF tones */
|
||||
x = 1;
|
||||
ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
|
||||
|
@ -916,13 +1013,13 @@ zapretry:
|
|||
ast_log(LOG_WARNING, "Could not find application (agi)\n");
|
||||
ret = -2;
|
||||
}
|
||||
if (!strcasecmp(chan->type,"Zap")) {
|
||||
if (user->zapchannel) {
|
||||
/* Remove CONFMUTE mode on Zap channel */
|
||||
x = 0;
|
||||
ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
|
||||
}
|
||||
} else {
|
||||
if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
|
||||
if (user->zapchannel && (confflags & CONFFLAG_STARMENU)) {
|
||||
/* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
|
||||
x = 1;
|
||||
ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
|
||||
|
@ -932,8 +1029,19 @@ zapretry:
|
|||
res = -1;
|
||||
}
|
||||
for(;;) {
|
||||
int menu_was_active = 0;
|
||||
|
||||
outfd = -1;
|
||||
ms = -1;
|
||||
|
||||
/* if we have just exited from the menu, and the user had a channel-driver
|
||||
volume adjustment, restore it
|
||||
*/
|
||||
if (!menu_active && menu_was_active && user->desired_volume && !user->actual_volume)
|
||||
set_volume(user, user->desired_volume);
|
||||
|
||||
menu_was_active = menu_active;
|
||||
|
||||
currentmarked = conf->markedusers;
|
||||
if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_MARKEDUSER) && (confflags & CONFFLAG_WAITMARKED) && lastmarked == 0) {
|
||||
if (currentmarked == 1 && conf->users > 1) {
|
||||
|
@ -1079,6 +1187,9 @@ zapretry:
|
|||
if (!f)
|
||||
break;
|
||||
if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
|
||||
if (user->actual_volume) {
|
||||
adjust_volume(f, user->actual_volume);
|
||||
}
|
||||
if (confflags & CONFFLAG_MONITORTALKER) {
|
||||
int totalsilence;
|
||||
if (user->talking == -1)
|
||||
|
@ -1129,6 +1240,13 @@ zapretry:
|
|||
ast_mutex_unlock(&conflock);
|
||||
goto outrun;
|
||||
}
|
||||
|
||||
/* if we are entering the menu, and the user has a channel-driver
|
||||
volume adjustment, clear it
|
||||
*/
|
||||
if (!menu_active && user->desired_volume && !user->actual_volume)
|
||||
set_volume(user, 0);
|
||||
|
||||
if (musiconhold) {
|
||||
ast_moh_stop(chan);
|
||||
}
|
||||
|
@ -1189,6 +1307,19 @@ zapretry:
|
|||
usr->adminflags |= ADMINFLAG_KICKME;
|
||||
ast_stopstream(chan);
|
||||
break;
|
||||
|
||||
case '9':
|
||||
tweak_volume(user, VOL_UP);
|
||||
break;
|
||||
|
||||
case '8':
|
||||
menu_active = 0;
|
||||
break;
|
||||
|
||||
case '7':
|
||||
tweak_volume(user, VOL_DOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
menu_active = 0;
|
||||
/* Play an error message! */
|
||||
|
@ -1232,6 +1363,18 @@ zapretry:
|
|||
ast_waitstream(chan, "");
|
||||
}
|
||||
break;
|
||||
case '9':
|
||||
tweak_volume(user, VOL_UP);
|
||||
break;
|
||||
|
||||
case '8':
|
||||
menu_active = 0;
|
||||
break;
|
||||
|
||||
case '7':
|
||||
tweak_volume(user, VOL_DOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
menu_active = 0;
|
||||
/* Play an error message! */
|
||||
|
|
|
@ -2992,18 +2992,29 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
|
|||
{
|
||||
struct ast_option_header *h;
|
||||
int res;
|
||||
h = malloc(datalen + sizeof(struct ast_option_header));
|
||||
if (h) {
|
||||
h->flag = AST_OPTION_FLAG_REQUEST;
|
||||
h->option = htons(option);
|
||||
memcpy(h->data, data, datalen);
|
||||
res = send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_CONTROL,
|
||||
AST_CONTROL_OPTION, 0, (unsigned char *)h, datalen + sizeof(struct ast_option_header), -1);
|
||||
free(h);
|
||||
return res;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Out of memory\n");
|
||||
return -1;
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_TXGAIN:
|
||||
case AST_OPTION_RXGAIN:
|
||||
/* these two cannot be sent, because they require a result */
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
default:
|
||||
h = malloc(datalen + sizeof(*h));
|
||||
if (h) {
|
||||
h->flag = AST_OPTION_FLAG_REQUEST;
|
||||
h->option = htons(option);
|
||||
memcpy(h->data, data, datalen);
|
||||
res = send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_CONTROL,
|
||||
AST_CONTROL_OPTION, 0, (unsigned char *) h,
|
||||
datalen + sizeof(*h), -1);
|
||||
free(h);
|
||||
return res;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct ast_frame *iax2_read(struct ast_channel *c)
|
||||
|
|
|
@ -1409,50 +1409,105 @@ static void zt_disable_ec(struct zt_pvt *p)
|
|||
p->echocanon = 0;
|
||||
}
|
||||
|
||||
static void fill_txgain(struct zt_gains *g, float gain, int law)
|
||||
{
|
||||
int j;
|
||||
short k;
|
||||
float linear_gain = pow(10.0, gain / 20.0);
|
||||
|
||||
switch (law) {
|
||||
case ZT_LAW_ALAW:
|
||||
for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) {
|
||||
if (gain) {
|
||||
k = (short) (((float) AST_ALAW(j)) * linear_gain);
|
||||
g->txgain[j] = AST_LIN2A(k);
|
||||
} else {
|
||||
g->txgain[j] = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZT_LAW_MULAW:
|
||||
for (j = 0; j < (sizeof(g->txgain) / sizeof(g->txgain[0])); j++) {
|
||||
if (gain) {
|
||||
k = (short) (((float) AST_MULAW(j)) * linear_gain);
|
||||
g->txgain[j] = AST_LIN2MU(k);
|
||||
} else {
|
||||
g->txgain[j] = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_rxgain(struct zt_gains *g, float gain, int law)
|
||||
{
|
||||
int j;
|
||||
short k;
|
||||
float linear_gain = pow(10.0, gain / 20.0);
|
||||
|
||||
switch (law) {
|
||||
case ZT_LAW_ALAW:
|
||||
for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) {
|
||||
if (gain) {
|
||||
k = (short) (((float) AST_ALAW(j)) * linear_gain);
|
||||
g->rxgain[j] = AST_LIN2A(k);
|
||||
} else {
|
||||
g->rxgain[j] = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZT_LAW_MULAW:
|
||||
for (j = 0; j < (sizeof(g->rxgain) / sizeof(g->rxgain[0])); j++) {
|
||||
if (gain) {
|
||||
k = (short) (((float) AST_MULAW(j)) * linear_gain);
|
||||
g->rxgain[j] = AST_LIN2MU(k);
|
||||
} else {
|
||||
g->rxgain[j] = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int set_actual_txgain(int fd, int chan, float gain, int law)
|
||||
{
|
||||
struct zt_gains g;
|
||||
int res;
|
||||
|
||||
memset(&g, 0, sizeof(g));
|
||||
g.chan = chan;
|
||||
res = ioctl(fd, ZT_GETGAINS, &g);
|
||||
if (res) {
|
||||
ast_log(LOG_DEBUG, "Failed to read gains: %s\n", strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
fill_txgain(&g, gain, law);
|
||||
|
||||
return ioctl(fd, ZT_SETGAINS, &g);
|
||||
}
|
||||
|
||||
int set_actual_rxgain(int fd, int chan, float gain, int law)
|
||||
{
|
||||
struct zt_gains g;
|
||||
int res;
|
||||
|
||||
memset(&g, 0, sizeof(g));
|
||||
g.chan = chan;
|
||||
res = ioctl(fd, ZT_GETGAINS, &g);
|
||||
if (res) {
|
||||
ast_log(LOG_DEBUG, "Failed to read gains: %s\n", strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
fill_rxgain(&g, gain, law);
|
||||
|
||||
return ioctl(fd, ZT_SETGAINS, &g);
|
||||
}
|
||||
|
||||
int set_actual_gain(int fd, int chan, float rxgain, float txgain, int law)
|
||||
{
|
||||
struct zt_gains g;
|
||||
float ltxgain;
|
||||
float lrxgain;
|
||||
int j,k;
|
||||
g.chan = chan;
|
||||
if ((rxgain != 0.0) || (txgain != 0.0)) {
|
||||
/* caluculate linear value of tx gain */
|
||||
ltxgain = pow(10.0,txgain / 20.0);
|
||||
/* caluculate linear value of rx gain */
|
||||
lrxgain = pow(10.0,rxgain / 20.0);
|
||||
if (law == ZT_LAW_ALAW) {
|
||||
for (j=0;j<256;j++) {
|
||||
k = (int)(((float)AST_ALAW(j)) * lrxgain);
|
||||
if (k > 32767) k = 32767;
|
||||
if (k < -32767) k = -32767;
|
||||
g.rxgain[j] = AST_LIN2A(k);
|
||||
k = (int)(((float)AST_ALAW(j)) * ltxgain);
|
||||
if (k > 32767) k = 32767;
|
||||
if (k < -32767) k = -32767;
|
||||
g.txgain[j] = AST_LIN2A(k);
|
||||
}
|
||||
} else {
|
||||
for (j=0;j<256;j++) {
|
||||
k = (int)(((float)AST_MULAW(j)) * lrxgain);
|
||||
if (k > 32767) k = 32767;
|
||||
if (k < -32767) k = -32767;
|
||||
g.rxgain[j] = AST_LIN2MU(k);
|
||||
k = (int)(((float)AST_MULAW(j)) * ltxgain);
|
||||
if (k > 32767) k = 32767;
|
||||
if (k < -32767) k = -32767;
|
||||
g.txgain[j] = AST_LIN2MU(k);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (j=0;j<256;j++) {
|
||||
g.rxgain[j] = j;
|
||||
g.txgain[j] = j;
|
||||
}
|
||||
}
|
||||
|
||||
/* set 'em */
|
||||
return(ioctl(fd,ZT_SETGAINS,&g));
|
||||
return set_actual_txgain(fd, chan, txgain, law) | set_actual_rxgain(fd, chan, rxgain, law);
|
||||
}
|
||||
|
||||
static inline int zt_set_hook(int fd, int hs)
|
||||
|
@ -2570,65 +2625,78 @@ static int zt_answer(struct ast_channel *ast)
|
|||
|
||||
static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen)
|
||||
{
|
||||
char *cp;
|
||||
int x;
|
||||
|
||||
char *cp;
|
||||
signed char *scp;
|
||||
int x;
|
||||
int index;
|
||||
struct zt_pvt *p = chan->tech_pvt;
|
||||
|
||||
|
||||
if ((option != AST_OPTION_TONE_VERIFY) && (option != AST_OPTION_AUDIO_MODE) &&
|
||||
(option != AST_OPTION_TDD) && (option != AST_OPTION_RELAXDTMF))
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
cp = (char *)data;
|
||||
if ((!cp) || (datalen < 1))
|
||||
{
|
||||
/* all supported options require data */
|
||||
if (!data || (datalen < 1)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch(option) {
|
||||
case AST_OPTION_TONE_VERIFY:
|
||||
case AST_OPTION_TXGAIN:
|
||||
scp = (signed char *) data;
|
||||
index = zt_get_index(chan, p, 0);
|
||||
if (index < 0) {
|
||||
ast_log(LOG_WARNING, "No index in TXGAIN?\n");
|
||||
return -1;
|
||||
}
|
||||
ast_log(LOG_DEBUG, "Setting actual tx gain on %s to %f\n", chan->name, p->txgain + (float) *scp);
|
||||
return set_actual_txgain(p->subs[index].zfd, 0, p->txgain + (float) *scp, p->law);
|
||||
case AST_OPTION_RXGAIN:
|
||||
scp = (signed char *) data;
|
||||
index = zt_get_index(chan, p, 0);
|
||||
if (index < 0) {
|
||||
ast_log(LOG_WARNING, "No index in RXGAIN?\n");
|
||||
return -1;
|
||||
}
|
||||
ast_log(LOG_DEBUG, "Setting actual rx gain on %s to %f\n", chan->name, p->rxgain + (float) *scp);
|
||||
return set_actual_rxgain(p->subs[index].zfd, 0, p->rxgain + (float) *scp, p->law);
|
||||
case AST_OPTION_TONE_VERIFY:
|
||||
if (!p->dsp)
|
||||
break;
|
||||
switch(*cp) {
|
||||
case 1:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | p->dtmfrelax); /* set mute mode if desired */
|
||||
cp = (char *) data;
|
||||
switch (*cp) {
|
||||
case 1:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | p->dtmfrelax); /* set mute mode if desired */
|
||||
break;
|
||||
case 2:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX | p->dtmfrelax); /* set mute mode if desired */
|
||||
case 2:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX | p->dtmfrelax); /* set mute mode if desired */
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); /* set mute mode if desired */
|
||||
default:
|
||||
ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name);
|
||||
ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax); /* set mute mode if desired */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_TDD: /* turn on or off TDD */
|
||||
case AST_OPTION_TDD:
|
||||
/* turn on or off TDD */
|
||||
cp = (char *) data;
|
||||
p->mate = 0;
|
||||
if (!*cp) { /* turn it off */
|
||||
ast_log(LOG_DEBUG, "Set option TDD MODE, value: OFF(0) on %s\n",chan->name);
|
||||
if (p->tdd) tdd_free(p->tdd);
|
||||
p->tdd = 0;
|
||||
p->mate = 0;
|
||||
break;
|
||||
}
|
||||
if (*cp == 2)
|
||||
ast_log(LOG_DEBUG, "Set option TDD MODE, value: MATE(2) on %s\n",chan->name);
|
||||
else ast_log(LOG_DEBUG, "Set option TDD MODE, value: ON(1) on %s\n",chan->name);
|
||||
p->mate = 0;
|
||||
ast_log(LOG_DEBUG, "Set option TDD MODE, value: %s(%d) on %s\n",
|
||||
(*cp == 2) ? "MATE" : "ON", (int) *cp, chan->name);
|
||||
zt_disable_ec(p);
|
||||
/* otherwise, turn it on */
|
||||
if (!p->didtdd) { /* if havent done it yet */
|
||||
unsigned char mybuf[41000],*buf;
|
||||
int size,res,fd,len;
|
||||
int index;
|
||||
struct pollfd fds[1];
|
||||
|
||||
buf = mybuf;
|
||||
memset(buf,0x7f,sizeof(mybuf)); /* set to silence */
|
||||
ast_tdd_gen_ecdisa(buf + 16000,16000); /* put in tone */
|
||||
memset(buf, 0x7f, sizeof(mybuf)); /* set to silence */
|
||||
ast_tdd_gen_ecdisa(buf + 16000, 16000); /* put in tone */
|
||||
len = 40000;
|
||||
index = zt_get_index(chan, p, 0);
|
||||
if (index < 0) {
|
||||
|
@ -2649,7 +2717,7 @@ int x;
|
|||
ast_log(LOG_DEBUG, "poll (for write) ret. 0 on channel %d\n", p->channel);
|
||||
continue;
|
||||
}
|
||||
/* if got exception */
|
||||
/* if got exception */
|
||||
if (fds[0].revents & POLLPRI) return -1;
|
||||
if (!(fds[0].revents & POLLOUT)) {
|
||||
ast_log(LOG_DEBUG, "write fd not ready on channel %d\n", p->channel);
|
||||
|
@ -2671,36 +2739,27 @@ int x;
|
|||
p->tdd = 0;
|
||||
p->mate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!p->tdd) { /* if we dont have one yet */
|
||||
p->tdd = tdd_new(); /* allocate one */
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_RELAXDTMF: /* Relax DTMF decoding (or not) */
|
||||
case AST_OPTION_RELAXDTMF: /* Relax DTMF decoding (or not) */
|
||||
if (!p->dsp)
|
||||
break;
|
||||
if (!*cp)
|
||||
{
|
||||
ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: OFF(0) on %s\n",chan->name);
|
||||
x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: ON(1) on %s\n",chan->name);
|
||||
x = 1;
|
||||
}
|
||||
ast_dsp_digitmode(p->dsp,x ? DSP_DIGITMODE_RELAXDTMF : DSP_DIGITMODE_DTMF | p->dtmfrelax);
|
||||
cp = (char *) data;
|
||||
ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: %s(%d) on %s\n",
|
||||
*cp ? "ON" : "OFF", (int) *cp, chan->name);
|
||||
ast_dsp_digitmode(p->dsp, ((*cp) ? DSP_DIGITMODE_RELAXDTMF : DSP_DIGITMODE_DTMF) | p->dtmfrelax);
|
||||
break;
|
||||
case AST_OPTION_AUDIO_MODE: /* Set AUDIO mode (or not) */
|
||||
if (!*cp)
|
||||
{
|
||||
ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: OFF(0) on %s\n",chan->name);
|
||||
case AST_OPTION_AUDIO_MODE: /* Set AUDIO mode (or not) */
|
||||
cp = (char *) data;
|
||||
if (!*cp) {
|
||||
ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: OFF(0) on %s\n", chan->name);
|
||||
x = 0;
|
||||
zt_disable_ec(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: ON(1) on %s\n",chan->name);
|
||||
} else {
|
||||
ast_log(LOG_DEBUG, "Set option AUDIO MODE, value: ON(1) on %s\n", chan->name);
|
||||
x = 1;
|
||||
}
|
||||
if (ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &x) == -1)
|
||||
|
@ -2708,6 +2767,7 @@ int x;
|
|||
break;
|
||||
}
|
||||
errno = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -227,6 +227,22 @@ struct ast_frame_chain {
|
|||
/* Set (or clear) Audio (Not-Clear) Mode */
|
||||
#define AST_OPTION_AUDIO_MODE 4
|
||||
|
||||
/* Set channel transmit gain */
|
||||
/* Option data is a single signed char
|
||||
representing number of decibels (dB)
|
||||
to set gain to (on top of any gain
|
||||
specified in channel driver)
|
||||
*/
|
||||
#define AST_OPTION_TXGAIN 5
|
||||
|
||||
/* Set channel receive gain */
|
||||
/* Option data is a single signed char
|
||||
representing number of decibels (dB)
|
||||
to set gain to (on top of any gain
|
||||
specified in channel driver)
|
||||
*/
|
||||
#define AST_OPTION_RXGAIN 6
|
||||
|
||||
struct ast_option_header {
|
||||
/* Always keep in network byte order */
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
|
Loading…
Reference in New Issue