Index: chan_oss.c =================================================================== --- chan_oss.c (.../vendor/asterisk-1.0.9/channels) (Revision 13) +++ chan_oss.c (.../trunk/asterisk/channels) (Arbeitskopie) @@ -15,6 +15,9 @@ * the GNU General Public License */ + +// THIS IS NOT THE OFFICIAL DRIVER CODE!! + #include #include #include @@ -22,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +38,7 @@ #include #include #include +#include #if defined( __OpenBSD__ ) # include @@ -61,9 +66,11 @@ #endif #include "busy.h" #include "ringtone.h" -#include "ring10.h" +//#include "ring10.h" #include "answer.h" +/* Remember to also set OPTIONS += -DRADIO_RELAX in Makefile! */ + /* Which device to use */ #if defined( __OpenBSD__ ) || defined( __NetBSD__ ) #define DEV_DSP "/dev/audio" @@ -113,11 +120,36 @@ int repeat; }; +/* Changes for DECT/analog telephone soundcard interface by Onno + Kortmann . Surely, some cleanups are still neccessary! + + Patch version: $Id$ + */ + +static short ring50hz[]={ + // 80 samples half-wave + + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + + // and 80 samples half-wave - + -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, + -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, + -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, + -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, + -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768 }; + + +/* FIXME: Order of this structure is important, do not change it without + checking all references to 'sounds'!! */ static struct sound sounds[] = { { AST_CONTROL_RINGING, ringtone, sizeof(ringtone)/2, 16000, 32000, 1 }, { AST_CONTROL_BUSY, busy, sizeof(busy)/2, 4000, 4000, 1 }, { AST_CONTROL_CONGESTION, busy, sizeof(busy)/2, 2000, 2000, 1 }, - { AST_CONTROL_RING, ring10, sizeof(ring10)/2, 16000, 32000, 1 }, + /* the following is replaced 50Hz square wave brzzz to let the phone ring (the real ringer!) */ + { AST_CONTROL_RING, ring50hz, sizeof(ring50hz)/2, 16000, 32000, 1 }, { AST_CONTROL_ANSWER, answer, sizeof(answer)/2, 2200, 0, 0 }, }; @@ -132,6 +164,136 @@ char context[AST_MAX_EXTENSION]; } oss; +struct ast_dsp *dsp; + +// number of frames to use for hook detection +#define HD_FRAME_INTEGRATION 3 + +// calc standard deviation of signal +static double hd_calc_stddev(short *buf, int len) { + int x; + // calculate mean + int meani=0; + double mean; + double dev=0.0; + + for (x=0; x < len; x++) + meani+=buf[x]; + mean=meani/len; + + for (x=0; x < len; x++) + dev+=(buf[x]-mean)*(buf[x]-mean); + + dev/=len; + return sqrt(dev); +} + +static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state); +static int oss_hangup(struct ast_channel *c); + +static int cursound = -1; +static int sampsent = 0; +static int silencelen=0; +static int offset=0; +static int nosound=0; + +/* Parameters for off hook detection. */ +static int hd_disp_interval=-1; +static int hl_threshold=40; // high level noise threshold +static int hl_ring_threshold=110; // high level noise threshold during ring +static int hl_seqmin=4; // 'low pass' for high level threshold +static int ll_threshold=40; // low level noise threshold +static int ll_seqmin=4; // 'low pass' for low level threshold +static int vol_divide=3; /* Volume divide to make the input volume of this driver right (to avoid + acoustical echos). */ + +// amplitude histogram off/on-hook detection +/*! \parameter len frame length in SAMPLES */ +static void amp_hist(short *frame, int len) { + static short pbuf[FRAME_SIZE*HD_FRAME_INTEGRATION]; + static int pbp; // pbuf pos (next write location) + int fpos=0; // frame position + do { + // concatenate data together to get full frames + int cpylen=len; + if (cpylen>FRAME_SIZE*HD_FRAME_INTEGRATION-pbp) + cpylen=FRAME_SIZE*HD_FRAME_INTEGRATION-pbp; + + memcpy(pbuf+pbp, frame+fpos, cpylen*2); + pbp+=cpylen; fpos+=cpylen; + + if (pbp==FRAME_SIZE*HD_FRAME_INTEGRATION) { + int x; + static FILE *f=NULL; + if (!f) f=fopen("/tmp/dump.raw", "wb"); + + double stddev=hd_calc_stddev(pbuf, FRAME_SIZE*HD_FRAME_INTEGRATION); + if (f) fwrite(pbuf, 1, FRAME_SIZE*HD_FRAME_INTEGRATION*2, f); + + /* For finding the sweet spot here, i.e. the correct + threshold values etc., a switchable debug output is + really neccessary. */ + static int disp_count; + disp_count++; + if (hd_disp_interval > 0 && (!(disp_count%hd_disp_interval))) + ast_verbose("stddev (for %d samples): %f\n", FRAME_SIZE*HD_FRAME_INTEGRATION, stddev); + + static int hd_state=0; /* hangup state. FIXME: Maybe + this can be combined with + hookstate? */ + + static int lo_seqcnt; // needs to be some value for real offline detecton + if (stddevll_seqmin) && (hd_state==1)) { + ast_verbose("stddev (for %d samples): %f\n", FRAME_SIZE*HD_FRAME_INTEGRATION, stddev); + ast_verbose("oss: HANGUP\n"); + if (oss.owner) ast_verbose("oss: current asterisk channel state: %d\n", oss.owner->_state); + hd_state=0; + // do hang up work + + hookstate=0; + cursound=-1; + nosound=1; + + if (oss.owner) { + ast_verbose("OSS: HANGUP ---> SEND HANGUP\n"); + ast_queue_hangup(oss.owner); + } + } + static int hi_seqcnt; // needs to be some value for real online detecton + if (stddev< hl_threshold || + ((cursound==3) && stddevhl_seqmin) && (hd_state==0)) { + ast_verbose("stddev (for %d samples): %f\n", FRAME_SIZE*HD_FRAME_INTEGRATION, stddev); + if (oss.owner) ast_verbose("oss: current asterisk channel state: %d\n", oss.owner->_state); + ast_verbose("oss: OFFHOOK\n"); + hd_state=1; + + // do off hook work + if (!oss.owner) { + ast_verbose("OSS: HOOK ---> AST_OFFHOOK\n"); + oss.owner=oss_new(&oss, AST_STATE_UP); + } + if (oss.owner) { + ast_verbose("OSS: HOOK -> HOOKSTATE=1\n"); + ast_setstate(oss.owner, AST_STATE_UP); + hookstate=1; + cursound = -1; + nosound=0; + } + hookstate=1; + } + pbp=0; + } + } while(fpos (abi.fragments * abi.fragsize / 2)) - total = abi.fragments * abi.fragsize / 2; + if (total > (abi.fragments * abi.fragsize/4)) + total = abi.fragments * abi.fragsize/4; res = total; if (sampsent < sounds[cursound].samplen) { myoff=0; @@ -237,8 +393,24 @@ } } } - if (frame) - res = write(sounddev, frame, res * 2); + if (frame) { + int i; + static short stereo[FRAME_SIZE*2]; + /* + if (cursound==3) + ast_verbose("Preparing ring tone.\n"); + else ast_verbose("Preparing normal tone.\n"); + */ + for (i=0; i < res; i++) + if (cursound==3) { // phone ring into right channel + stereo[2*i]=0; + stereo[2*i+1]=frame[i]; + } else { + stereo[2*i]=frame[i]; + stereo[2*i+1]=0; + } + res=write(sounddev, stereo, res*4); + } if (res > 0) return 0; return res; @@ -253,8 +425,8 @@ int max; int res; char ign[4096]; - if (read(sounddev, ign, sizeof(sounddev)) < 0) - ast_log(LOG_WARNING, "Read error on sound device: %s\n", strerror(errno)); + if (read(sounddev, ign, 4) < 0) + ast_log(LOG_WARNING, "Read error on sound device: %s\n", strerror(errno)); for(;;) { FD_ZERO(&rfds); FD_ZERO(&wfds); @@ -262,6 +434,10 @@ FD_SET(sndcmd[0], &rfds); if (!oss.owner) { FD_SET(sounddev, &rfds); + FD_SET(sounddev, &wfds); /* to get real data, some + soundcards require writing + data simultaneously. */ + //ast_verbose("Setting rfds wfds.\n"); if (sounddev > max) max = sounddev; } @@ -282,13 +458,42 @@ sampsent = 0; } if (FD_ISSET(sounddev, &rfds)) { - /* Ignore read */ - if (read(sounddev, ign, sizeof(ign)) < 0) + int rlen=0; + if ((rlen=read(sounddev, ign, sizeof(ign))) < 0) { ast_log(LOG_WARNING, "Read error on sound device: %s\n", strerror(errno)); + } else { + static short mono[1024]; + int i; + for (i=0; i < rlen/4; i++) + mono[i]=((short*)ign)[2*i]; + //ast_verbose("%d bytes to amp_hist.\n", rlen); + amp_hist((short*)mono, rlen/4); + } } - if (FD_ISSET(sounddev, &wfds)) - if (send_sound()) + if (FD_ISSET(sounddev, &wfds)) { + if (cursound==-1) { + /* + audio_buf_info abi; + // write dummy data to keep the sound card + // happy in receive mode! + int res = ioctl(sounddev, SNDCTL_DSP_GETOSPACE ,&abi); + if (res) { + ast_log(LOG_WARNING, "Failed to do power surveillance dummy write."); + continue; + } + res=sizeof(ign); + ast_verbose("abi.fragments=%d, abi.fragsize=%d\n", abi.fragments, abi.fragsize); + if (res>(abi.fragments * abi.fragsize)) + res=abi.fragments * abi.fragsize; + + memset(ign, 0, res); + res=write(sounddev, ign, res); + ast_verbose("Wrote %d bytes dummy data\n", res); +*/ + } else if (send_sound()) ast_log(LOG_WARNING, "Failed to write sound\n"); + } + } /* Never reached */ return NULL; @@ -327,7 +532,7 @@ /* Write any buffered silence we have, it may have something important */ if (silbufcnt) { - write(sounddev, silbuf, silbufcnt * FRAME_SIZE); + FIX THIS WRITE WITH STEREO SUPPORT!! write(sounddev, silbuf, silbufcnt * FRAME_SIZE); silbufcnt = 0; } } @@ -362,10 +567,10 @@ ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n"); full_duplex = -1; } - fmt = 0; - res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt); - if (res < 0) { - ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); + fmt=2; + res=ioctl(fd, SNDCTL_DSP_CHANNELS, &fmt); + if ((res < 0) || (fmt!=2)) { + ast_log(LOG_WARNING, "Failed to set audio device to stereo\n"); return -1; } /* 8000 Hz desired */ @@ -373,7 +578,7 @@ fmt = desired; res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt); if (res < 0) { - ast_log(LOG_WARNING, "Failed to set audio device to mono\n"); + ast_log(LOG_WARNING, "Failed to set audio device to 8000Hz sample rate\n"); return -1; } if (fmt != desired) { @@ -483,19 +688,11 @@ int res = 3; struct ast_frame f = { 0, }; ast_verbose( " << Call placed to '%s' on console >> \n", dest); - if (autoanswer) { - ast_verbose( " << Auto-answered >> \n" ); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_ANSWER; - ast_queue_frame(c, &f); - } else { - nosound = 1; - ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n"); - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_RINGING; - ast_queue_frame(c, &f); - write(sndcmd[1], &res, sizeof(res)); - } + nosound = 1; + f.frametype = AST_FRAME_CONTROL; + f.subclass = AST_CONTROL_RINGING; + ast_queue_frame(c, &f); + write(sndcmd[1], &res, sizeof(res)); return 0; } @@ -520,28 +717,26 @@ static int oss_hangup(struct ast_channel *c) { - int res = 0; - cursound = -1; - c->pvt->pvt = NULL; - oss.owner = NULL; - ast_verbose( " << Hangup on console >> \n"); - ast_mutex_lock(&usecnt_lock); - usecnt--; - ast_mutex_unlock(&usecnt_lock); - if (hookstate) { - if (autoanswer) { - /* Assume auto-hangup too */ - hookstate = 0; - } else { - /* Make congestion noise */ - res = 2; - write(sndcmd[1], &res, sizeof(res)); - } - } - return 0; + int res = 0; + cursound = -1; + c->pvt->pvt = NULL; + oss.owner = NULL; + ast_verbose( " << Hangup on console >> \n"); + ast_mutex_lock(&usecnt_lock); + usecnt--; + ast_mutex_unlock(&usecnt_lock); + + if (hookstate) + res=2; // say 'busy' to the phone if it is still online + else + // ensure that the sound card thread runs + res=-1; + + write(sndcmd[1], &res, sizeof(res)); + return 0; } -static int soundcard_writeframe(short *data) + int soundcard_writeframe(short *data) { /* Write an exactly FRAME_SIZE sized of frame */ static int bufcnt = 0; @@ -556,20 +751,38 @@ bufcnt = buffersize; warned++; } - if ((info.fragments >= buffersize * 5) && (bufcnt == buffersize)) { + if (((info.fragments/2) >= buffersize * 5) && (bufcnt == buffersize)) { /* We've run out of stuff, buffer again */ bufcnt = 0; } if (bufcnt == buffersize) { /* Write sample immediately */ - res = write(fd, ((void *)data), FRAME_SIZE * 2); + static short stereo[FRAME_SIZE*2]; + int i; + //ast_verbose("writeframe normal\n"); + for (i=0; i < FRAME_SIZE; i++) { + stereo[2*i]=data[i]; + stereo[2*i+1]=0; + } + res = write(fd, stereo, FRAME_SIZE * 4); + res=0; + if (res>0) res/=2; } else { /* Copy the data into our buffer */ res = FRAME_SIZE * 2; memcpy(buffer + (bufcnt * FRAME_SIZE), data, FRAME_SIZE * 2); bufcnt++; if (bufcnt == buffersize) { - res = write(fd, ((void *)buffer), FRAME_SIZE * 2 * buffersize); + //ast_verbose("writeframe big\n"); + short stereo[FRAME_SIZE*2*buffersize]; + int i; + for (i=0; i < FRAME_SIZE*buffersize; i++) { + stereo[2*i]=buffer[i]; + stereo[2*i+1]=0; + } + res = write(fd, stereo, FRAME_SIZE * 4 * buffersize); + res=0; + if (res>0) res/=2; } } return res; @@ -656,7 +869,16 @@ /* Theoretically shouldn't happen, but anyway, return a NULL frame */ return &f; } - res = read(sounddev, buf + AST_FRIENDLY_OFFSET + readpos, FRAME_SIZE * 2 - readpos); + + short stereo[FRAME_SIZE*2]; + res=read(sounddev, stereo, FRAME_SIZE*4-readpos*2); + if (res>0) { + int i; + for (i=0; i= FRAME_SIZE * 2) { + int x; /* A real frame */ readpos = 0; + amp_hist((short*)(buf+AST_FRIENDLY_OFFSET), FRAME_SIZE); + for (x=0; x < FRAME_SIZE; x++) { + ((short*)(buf+AST_FRIENDLY_OFFSET))[x]/=vol_divide; + } if (chan->_state != AST_STATE_UP) { + //ast_verbose("oss: Data received but channel is not up (%d)!\n", chan->_state); /* Don't transmit unless it's up */ return &f; } @@ -683,14 +910,28 @@ f.mallocd = 0; f.delivery.tv_sec = 0; f.delivery.tv_usec = 0; + + // ast_verbose("loudness %d\n", power_state); #if 0 { static int fd = -1; if (fd < 0) fd = open("output.raw", O_RDWR | O_TRUNC | O_CREAT); - write(fd, f.data, f.datalen); + FIX THIS WRITE, CHANGES FOR STEREO!! write(fd, f.data, f.datalen); } #endif } + + /* process with dsp for DTMF */ + if (dsp) { + struct ast_frame* f2 = ast_dsp_process(chan, dsp, &f); + //FIXME: Check f2==&f ? + // ast_verbose("Checked for DTMF data.\n"); + if (f2 && (f2->frametype == AST_FRAME_DTMF)) { + ast_verbose("Detected inband DTMF digit: %c on OSS\n", f2->subclass); + return f2; + } + } + return &f; } @@ -766,6 +1007,17 @@ } } } + /* Let AST DSP detect DTMF */ + if (dsp) { + ast_verbose("Already have a dsp for OSS?\n"); + } else { + dsp=ast_dsp_new(); + if (dsp) { + ast_verbose("Detecting DTMF inband with sw DSP on OSS\n"); + ast_dsp_set_features(dsp, DSP_FEATURE_DTMF_DETECT); + ast_dsp_digitmode(dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF); + } + } return tmp; } @@ -789,48 +1041,6 @@ return tmp; } -static int console_autoanswer(int fd, int argc, char *argv[]) -{ - if ((argc != 1) && (argc != 2)) - return RESULT_SHOWUSAGE; - if (argc == 1) { - ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off"); - return RESULT_SUCCESS; - } else { - if (!strcasecmp(argv[1], "on")) - autoanswer = -1; - else if (!strcasecmp(argv[1], "off")) - autoanswer = 0; - else - return RESULT_SHOWUSAGE; - } - return RESULT_SUCCESS; -} - -static char *autoanswer_complete(char *line, char *word, int pos, int state) -{ -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - switch(state) { - case 0: - if (strlen(word) && !strncasecmp(word, "on", MIN(strlen(word), 2))) - return strdup("on"); - case 1: - if (strlen(word) && !strncasecmp(word, "off", MIN(strlen(word), 3))) - return strdup("off"); - default: - return NULL; - } - return NULL; -} - -static char autoanswer_usage[] = -"Usage: autoanswer [on|off]\n" -" Enables or disables autoanswer feature. If used without\n" -" argument, displays the current on/off status of autoanswer.\n" -" The default value of autoanswer is in 'oss.conf'.\n"; - static int console_answer(int fd, int argc, char *argv[]) { struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; @@ -990,7 +1200,6 @@ { { "dial", NULL }, console_dial, "Dial an extension on the console", dial_usage }, { { "transfer", NULL }, console_transfer, "Transfer a call to a different extension", transfer_usage }, { { "send", "text", NULL }, console_sendtext, "Send text to the remote device", sendtext_usage }, - { { "autoanswer", NULL }, console_autoanswer, "Sets/displays autoanswer", autoanswer_usage, autoanswer_complete } }; int load_module() @@ -1024,9 +1233,7 @@ if ((cfg = ast_load(config))) { v = ast_variable_browse(cfg, "general"); while(v) { - if (!strcasecmp(v->name, "autoanswer")) - autoanswer = ast_true(v->value); - else if (!strcasecmp(v->name, "silencesuppression")) + if (!strcasecmp(v->name, "silencesuppression")) silencesuppression = ast_true(v->value); else if (!strcasecmp(v->name, "silencethreshold")) silencethreshold = atoi(v->value); @@ -1036,6 +1243,20 @@ strncpy(language, v->value, sizeof(language)-1); else if (!strcasecmp(v->name, "extension")) strncpy(exten, v->value, sizeof(exten)-1); + else if (!strcasecmp(v->name, "hd_disp_interval")) + hd_disp_interval=atoi(v->value); + else if (!strcasecmp(v->name, "hd_hl_threshold")) + hl_threshold=atoi(v->value); + else if (!strcasecmp(v->name, "hd_hl_ring_threshold")) + hl_ring_threshold=atoi(v->value); + else if (!strcasecmp(v->name, "hd_hl_seqmin")) + hl_seqmin=atoi(v->value); + else if (!strcasecmp(v->name, "hd_ll_threshold")) + ll_threshold=atoi(v->value); + else if (!strcasecmp(v->name, "hd_ll_seqmin")) + ll_threshold=atoi(v->value); + else if (!strcasecmp(v->name, "vol_divide")) + vol_divide=atoi(v->value); v=v->next; } ast_destroy(cfg); @@ -1081,3 +1302,8 @@ { return ASTERISK_GPL_KEY; } +/* + Local variables: + c-basic-offset: 8 + End: +*/ Eigenschaftsänderungen: chan_oss.c ___________________________________________________________________ Name: svn:keywords + Id Index: chan_oss.TODO =================================================================== --- chan_oss.TODO (.../vendor/asterisk-1.0.9/channels) (Revision 0) +++ chan_oss.TODO (.../trunk/asterisk/channels) (Revision 58) @@ -0,0 +1,6 @@ +- Implement left/right channel swapping +- New driver, new name? +- Removal of half-duplex soundcard code? +- Removal of console call commands? +- Cleanups of read/write! +- Multiple interfaces Eigenschaftsänderungen: ___________________________________________________________________ Name: svn:ignore + .depend *.so busy.h gentone ringtone.h