X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=port.cpp;h=d254d0cd5fb874c5b212e4fdefd8fdc75f6c6bd9;hp=a3a33c8c6685c5cfcef9fe23aff9856dec9bdb15;hb=a0beafd25e37f01f404ee0020d076d0ecc01ada8;hpb=fd2045584f7084d209607f4d717a66bea9afe88e diff --git a/port.cpp b/port.cpp index a3a33c8..d254d0c 100644 --- a/port.cpp +++ b/port.cpp @@ -9,6 +9,42 @@ ** ** \*****************************************************************************/ +/* HOW TO audio? + +Audio flow has two ways: + +* from channel to the upper layer + -> sound from mISDN channel + -> announcement from vbox channel + +* from the upper layer to the channel + -> sound from remote channel + +Audio is required: + + -> if local or remote channel is not mISDN + -> if call is recorded (vbox) + + +Functions: + +* PmISDN::txfromup + -> audio from upper layer is buffered for later transmission to channel +* PmISDN::handler + -> buffered audio from upper layer or tones are transmitted via system clock +* mISDN_handler + -> rx-data from port to record() and upper layer + -> tx-data from port (dsp) to record() +* VboxPort::handler + -> streaming announcement to upper layer + -> recording announcement +* VboxPort::message_epoint + -> recording audio message from upper layer + + + +*/ + #include #include #include @@ -17,11 +53,11 @@ #include #include #include +#include #include "main.h" -#define BETTERDELAY - -//#define MIXER_DEBUG /* debug mixer buffer overflow and underrun */ +#define SHORT_MIN -32768 +#define SHORT_MAX 32767 class Port *port_first = NULL; @@ -54,8 +90,7 @@ void Port::free_epointlist(struct epoint_list *epointlist) /* free */ PDEBUG(DEBUG_EPOINT, "PORT(%d) removed epoint from port\n", p_serial); - memset(temp, 0, sizeof(struct epoint_list)); - free(temp); + FREE(temp, sizeof(struct epoint_list)); ememuse--; } @@ -76,7 +111,7 @@ void Port::free_epointid(unsigned long epoint_id) } if (temp == 0) { - PERROR("epoint_id not in port's list, exitting.\n"); + PERROR("epoint_id not in port's list.\n"); return; } /* detach */ @@ -84,8 +119,7 @@ void Port::free_epointid(unsigned long epoint_id) /* free */ PDEBUG(DEBUG_EPOINT, "PORT(%d) removed epoint from port\n", p_serial); - memset(temp, 0, sizeof(struct epoint_list)); - free(temp); + FREE(temp, sizeof(struct epoint_list)); ememuse--; } @@ -97,15 +131,11 @@ struct epoint_list *Port::epointlist_new(unsigned long epoint_id) struct epoint_list *epointlist, **epointlistpointer; /* epointlist structure */ - epointlist = (struct epoint_list *)calloc(1, sizeof(struct epoint_list)); + epointlist = (struct epoint_list *)MALLOC(sizeof(struct epoint_list)); if (!epointlist) - { - PERROR("no mem for allocating epoint_list\n"); - return(0); - } + FATAL("No memory for epointlist\n"); ememuse++; PDEBUG(DEBUG_EPOINT, "PORT(%d) allocating epoint_list.\n", p_serial); - memset(epointlist, 0, sizeof(struct epoint_list)); /* add epoint_list to chain */ epointlist->next = NULL; @@ -141,15 +171,11 @@ Port::Port(int type, char *portname, struct port_settings *settings) } SCPY(p_name, portname); SCPY(p_tone_dir, p_settings.tones_dir); // just to be sure - p_clock = 0; p_type = type; p_serial = port_serial++; - p_debug_nothingtosend = 0; p_tone_fh = -1; p_tone_fetched = NULL; p_tone_name[0] = '\0'; -// p_knock_fh = -1; -// p_knock_fetched = NULL; p_state = PORT_STATE_IDLE; p_epointlist = NULL; memset(&p_callerinfo, 0, sizeof(p_callerinfo)); @@ -157,19 +183,20 @@ Port::Port(int type, char *portname, struct port_settings *settings) memset(&p_connectinfo, 0, sizeof(p_connectinfo)); memset(&p_redirinfo, 0, sizeof(p_redirinfo)); memset(&p_capainfo, 0, sizeof(p_capainfo)); - memset(p_mixer_buffer, 0, sizeof(p_mixer_buffer)); - memset(p_record_buffer, 0, sizeof(p_record_buffer)); - memset(p_stereo_buffer, 0, sizeof(p_stereo_buffer)); - p_mixer_rel = NULL; - p_mixer_readp = 0; p_echotest = 0; - next = NULL; + + /* call recording */ p_record = NULL; p_record_type = 0; p_record_length = 0; + p_record_skip = 0; p_record_filename[0] = '\0'; + p_record_buffer_readp = 0; + p_record_buffer_writep = 0; + p_record_buffer_dir = 0; /* append port to chain */ + next = NULL; temp = port_first; tempp = &port_first; while(temp) @@ -188,29 +215,16 @@ Port::Port(int type, char *portname, struct port_settings *settings) */ Port::~Port(void) { - struct mixer_relation *relation, *rtemp; class Port *temp, **tempp; struct message *message; if (p_record) - close_record(0); + close_record(0, 0); classuse--; PDEBUG(DEBUG_PORT, "removing port of type %d, name '%s'\n", p_type, p_name); - /* free mixer relation chain */ - relation = p_mixer_rel; - while(relation) - { - rtemp = relation; - relation = relation->next; - memset(rtemp, 0, sizeof(struct mixer_relation)); - free(rtemp); - pmemuse--; - } - p_mixer_rel = NULL; /* beeing paranoid */ - /* disconnect port from endpoint */ while(p_epointlist) { @@ -234,10 +248,7 @@ Port::~Port(void) temp = temp->next; } if (temp == NULL) - { - PERROR("PORT(%s) port not in port's list.\n", p_name); - exit(-1); - } + FATAL("PORT(%s) port not in port's list.\n", p_name); /* detach */ *tempp=this->next; @@ -474,11 +485,11 @@ void Port::set_vbox_speed(int speed) /* * read from the given file as specified in port_set_tone and return sample data + * if the tone ends, the result may be less samples than requested */ int Port::read_audio(unsigned char *buffer, int length) { int l,len; - int readp; int nodata=0; /* to detect 0-length files and avoid endless reopen */ char filename[128]; int tone_left_before; /* temp variable to determine the change in p_tone_left */ @@ -488,15 +499,10 @@ int Port::read_audio(unsigned char *buffer, int length) return(0); len = length; - codec_in = p_tone_codec; /* if there is no tone set, use silence */ - if (p_tone_name[0] == 0) - { -rest_is_silence: - memset(buffer, (options.law=='a')?0x2a:0xff, len); /* silence */ - goto done; - } + if (!p_tone_name[0]) + return(0); /* if the file pointer is not open, we open it */ if (p_tone_fh<0 && p_tone_fetched==NULL) @@ -566,7 +572,7 @@ read_more: } if (len==0) - goto done; + return(length-len); if (p_tone_fh >= 0) { @@ -585,7 +591,7 @@ read_more: PDEBUG(DEBUG_PORT, "PORT(%s) 0-length loop: %s\n", p_name, filename); p_tone_name[0]=0; p_tone_dir[0]=0; - goto rest_is_silence; + return(length-len); } /* if eof is reached, or if the normal file cannot be opened, continue with the loop file if possible */ @@ -610,8 +616,7 @@ try_loop: PDEBUG(DEBUG_PORT, "PORT(%s) no tone loop: %s\n",p_name, filename); p_tone_dir[0] = '\0'; p_tone_name[0] = '\0'; - // codec_in = CODEC_LAW; - goto rest_is_silence; + return(length-len); } fhuse++; } @@ -624,8 +629,7 @@ try_loop: PDEBUG(DEBUG_PORT, "PORT(%s) no tone loop: %s\n",p_name, filename); p_tone_dir[0] = '\0'; p_tone_name[0] = '\0'; - // codec_in = CODEC_LAW; - goto rest_is_silence; + return(length-len); } fhuse++; } @@ -634,9 +638,6 @@ try_loop: /* now we have opened the loop */ goto read_more; - -done: - return(length); } @@ -644,8 +645,6 @@ done: * process transmission clock */ int Port::handler(void) { - port - return(0); } @@ -664,11 +663,6 @@ int Port::message_epoint(unsigned long epoint_id, int message_id, union paramete set_tone(param->tone.dir,param->tone.name); return(1); - case MESSAGE_DATA: -//printf("port=%s, epoint=%d\n",p_cardname, epoint->e_serial); - mixer(param); - return(1); - case MESSAGE_VBOX_TONE: /* play tone of answering machine */ PDEBUG(DEBUG_PORT, "PORT(%s) set answering machine tone '%s' '%s'\n", p_name, param->tone.dir, param->tone.name); set_vbox_tone(param->tone.dir, param->tone.name); @@ -690,34 +684,6 @@ int Port::message_epoint(unsigned long epoint_id, int message_id, union paramete } -/* - * special function generate individual isdn debug logs - */ -void Port::printisdn(char *fmt, ...) -{ - char buffer[4096]; - char name[128]; - va_list args; - FILE *fp; - - va_start(args,fmt); - VUNPRINT(buffer,sizeof(buffer)-1,fmt,args); - buffer[sizeof(buffer)-1]=0; - va_end(args); - - PDEBUG_RUNTIME(NULL, 0, DEBUG_PORT, "PORT(%s serial=%ld): %s\n", p_name, p_serial, buffer); - if (options.deb & DEBUG_LOG) - { - SPRINT(name, "%s/debug_%s.log", INSTALL_DATA, p_name); - if (!(fp = fopen(name, "a"))) - return; - - fprintf(fp, "%04d.%02d.%02d %02d:%02d:%02d %s(%ld): %s", now_tm->tm_year+1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, p_name, p_serial, buffer); - fclose(fp); - } -} - - /* wave header structure */ struct fmt { unsigned short stereo; /* 1 = mono, 2 = stereo */ @@ -734,18 +700,18 @@ struct fmt { * written before close, because we do not know the size yet) * type=1 record annoucement, type=0 record audio stream, type=2 record vbox */ -int Port::open_record(int type, int vbox, int skip, char *terminal, int anon_ignore, char *vbox_email, int vbox_email_file) +int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ignore, char *vbox_email, int vbox_email_file) { /* RIFFxxxxWAVEfmt xxxx(fmt-size)dataxxxx... */ char dummyheader[8+4+8+sizeof(fmt)+8]; char filename[256]; - if (!terminal) + if (!extension) { - PERROR("Port(%d) not a terminal\n", p_serial); + PERROR("Port(%d) not an extension\n", p_serial); return(0); } - SCPY(p_record_extension, terminal); + SCPY(p_record_extension, extension); p_record_anon_ignore = anon_ignore; SCPY(p_record_vbox_email, vbox_email); p_record_vbox_email_file = vbox_email_file; @@ -823,10 +789,9 @@ int Port::open_record(int type, int vbox, int skip, char *terminal, int anon_ign /* * close the recoding file, put header in front and rename */ -void Port::close_record(int beep) +void Port::close_record(int beep, int mute) { - static signed long beep_mono[] = {-10000, 10000, -10000, 10000, -10000, 10000, -10000, 10000, -10000, 10000, -10000, 10000, -10000, 10000, -10000, 10000}; - static unsigned char beep_8bit[] = {48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208, 48, 208}; + static signed short beep_mono[256]; unsigned long size, wsize; struct fmt fmt; char filename[512], indexname[512]; @@ -839,11 +804,12 @@ void Port::close_record(int beep) if (!p_record) return; + PDEBUG(DEBUG_PORT, "data still in record buffer: %d (dir %d)\n", (p_record_buffer_writep - p_record_buffer_readp) & RECORD_BUFFER_MASK, p_record_buffer_dir); memcpy(&callerinfo, &p_callerinfo, sizeof(struct caller_info)); - apply_callerid_restriction(p_record_anon_ignore, -1, callerinfo.id, &callerinfo.ntype, &callerinfo.present, &callerinfo.screen, callerinfo.voip, callerinfo.intern, callerinfo.name); +// apply_callerid_restriction(p_record_anon_ignore, callerinfo.id, &callerinfo.ntype, &callerinfo.present, &callerinfo.screen, callerinfo.extension, callerinfo.name); - SCPY(number, p_dialinginfo.number); + SCPY(number, p_dialinginfo.id); SCPY(callerid, numberrize_callerinfo(callerinfo.id, callerinfo.ntype)); if (callerid[0] == '\0') { @@ -883,41 +849,31 @@ void Port::close_record(int beep) i++; } + /* mute */ + if (mute && p_record_type==CODEC_MONO) + { + i = p_record_length; + if (i > mute) + i = mute; + fseek(p_record, -(i<<1), SEEK_END); + p_record_length -= (i<<1); + } /* add beep to the end of recording */ - if (beep) - switch(p_record_type) + if (beep && p_record_type==CODEC_MONO) { - case CODEC_MONO: i = 0; - while(i < beep) + while(i < 256) { - fwrite(beep_mono, sizeof(beep_mono), 1, p_record); - i += sizeof(beep_mono); - p_record_length += sizeof(beep_mono); + beep_mono[i] = (signed short)(sin((double)i / 5.688888888889 * 2.0 * 3.1415927) * 2000.0); + i++; } - break; - case CODEC_8BIT: i = 0; while(i < beep) { - fwrite(beep_8bit, sizeof(beep_8bit), 1, p_record); - i += sizeof(beep_8bit); - p_record_length += sizeof(beep_8bit); - } - break; -#if 0 - case CODEC_LAW: - i = 0; - while(i < beep) - { - fwrite(beep_law, sizeof(beep_law), 1, p_record); - i += sizeof(beep_law); - p_record_length += sizeof(beep_law); + fwrite(beep_mono, sizeof(beep_mono), 1, p_record); + i += sizeof(beep_mono); + p_record_length += sizeof(beep_mono); } - break; -#endif - default: - PERROR("codec %d not supported for beep adding\n", p_record_type); } /* complete header */ @@ -995,12 +951,6 @@ void Port::close_record(int beep) else SPRINT(filename, "%s_%s-%s.isdn", p_record_filename, callerid, number); break; - - default: - if (p_record_vbox == 1) - SPRINT(filename, "%s.unknown", p_record_filename); - else - SPRINT(filename, "%s_%s-%s.unknown", p_record_filename, callerid, number); } fclose(p_record); @@ -1038,10 +988,255 @@ void Port::close_record(int beep) /* send email with sample*/ if (p_record_vbox_email[0]) { - send_mail(p_record_vbox_email_file?filename:(char *)"", callerid, callerinfo.intern, callerinfo.name, p_record_vbox_email, p_record_vbox_year, p_record_vbox_mon, p_record_vbox_mday, p_record_vbox_hour, p_record_vbox_min, p_record_extension); + send_mail(p_record_vbox_email_file?filename:(char *)"", callerid, callerinfo.extension, callerinfo.name, p_record_vbox_email, p_record_vbox_year, p_record_vbox_mon, p_record_vbox_mday, p_record_vbox_hour, p_record_vbox_min, p_record_extension); } } } +/* + * recording function + * Records all data from down and from up into one single stream. + * Both streams may have gaps or jitter. + * A Jitter buffer for both streams is used to compensate jitter. + * + * If one stream (dir) received packets, they are stored to a + * buffer to wait for the other stream (dir), so both streams can + * be combined. If the buffer is full, it's content is written + * without mixing stream. (assuming only one stram (dir) exists.) + * A flag is used to indicate what stream is currently in buffer. + * + * NOTE: First stereo sample (odd) is from down, second is from up. + */ +void Port::record(unsigned char *data, int length, int dir_fromup) +{ + unsigned char write_buffer[1024], *d; + signed short *s; + int free, i, ii; + signed long sample; + + /* no recording */ + if (!p_record || !length) + return; + + /* skip data from local caller (dtmf input) */ + if (p_record_skip && !dir_fromup) + { + /* more to skip than we have */ + if (p_record_skip > length) + { + p_record_skip -= length; + return; + } + /* less to skip */ + data += p_record_skip; + length -= p_record_skip; + p_record_skip = 0; + } + +//printf("dir=%d len=%d\n", dir_fromup, length); + + free = ((p_record_buffer_readp - p_record_buffer_writep - 1) & RECORD_BUFFER_MASK); + +//PDEBUG(DEBUG_PORT, "record(data,%d,%d): free=%d, p_record_buffer_dir=%d, p_record_buffer_readp=%d, p_record_buffer_writep=%d.\n", length, dir_fromup, free, p_record_buffer_dir, p_record_buffer_readp, p_record_buffer_writep); + + /* the buffer stores the same data stream */ + if (dir_fromup == p_record_buffer_dir) + { +same_again: + +//printf("same free=%d length=%d\n", free, length); + /* first write what we can to the buffer */ + while(free && length) + { + p_record_buffer[p_record_buffer_writep] = audio_law_to_s32[*data++]; + p_record_buffer_writep = (p_record_buffer_writep + 1) & RECORD_BUFFER_MASK; + free--; + length--; + } + /* all written, so we return */ + if (!length) + return; + /* still data left, buffer is full, so we need to write a chunk to file */ + switch(p_record_type) + { + case CODEC_MONO: + s = (signed short *)write_buffer; + i = 0; + while(i < 256) + { + *s++ = p_record_buffer[p_record_buffer_readp]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + fwrite(write_buffer, 512, 1, p_record); + p_record_length += 512; + break; + + case CODEC_STEREO: + s = (signed short *)write_buffer; + if (p_record_buffer_dir) + { + i = 0; + while(i < 256) + { + *s++ = 0; /* nothing from down */ + *s++ = p_record_buffer[p_record_buffer_readp]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + } else + { + i = 0; + while(i < 256) + { + *s++ = p_record_buffer[p_record_buffer_readp]; + *s++ = 0; /* nothing from up */ + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + } + fwrite(write_buffer, 1024, 1, p_record); + p_record_length += 1024; + break; + + case CODEC_8BIT: + d = write_buffer; + i = 0; + while(i < 256) + { + *d++ = ((unsigned short)(p_record_buffer[p_record_buffer_readp]+0x8000)) >> 8; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + fwrite(write_buffer, 512, 1, p_record); + p_record_length += 512; + break; + + case CODEC_LAW: + d = write_buffer; + i = 0; + while(i < 256) + { + *d++ = audio_s16_to_law[p_record_buffer[p_record_buffer_readp] & 0xffff]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + fwrite(write_buffer, 256, 1, p_record); + p_record_length += 256; + break; + } + /* because we still have data, we write again */ + free += 256; + goto same_again; + } + /* the buffer stores the other stream */ + +different_again: + /* if buffer empty, change it */ + if (p_record_buffer_readp == p_record_buffer_writep) + { + p_record_buffer_dir = dir_fromup; + goto same_again; + } + /* how much data can we mix ? */ + ii = (p_record_buffer_writep - p_record_buffer_readp) & RECORD_BUFFER_MASK; + if (length < ii) + ii = length; + + if (ii > 256) + ii = 256; +//printf("same ii=%d length=%d\n", ii, length); +//PDEBUG(DEBUG_PORT, "record(data,%d,%d): free=%d, p_record_buffer_dir=%d, p_record_buffer_readp=%d, p_record_buffer_writep=%d: mixing %d bytes.\n", length, dir_fromup, free, p_record_buffer_dir, p_record_buffer_readp, p_record_buffer_writep, ii); + + /* write data mixed with the buffer */ + switch(p_record_type) + { + case CODEC_MONO: + s = (signed short *)write_buffer; + i = 0; + while(i < ii) + { + sample = p_record_buffer[p_record_buffer_readp] + + audio_law_to_s32[*data++]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + if (sample < SHORT_MIN) sample = SHORT_MIN; + if (sample > SHORT_MAX) sample = SHORT_MAX; + *s++ = sample; + i++; + } + fwrite(write_buffer, ii<<1, 1, p_record); + p_record_length += (ii<<1); + break; + + case CODEC_STEREO: + s = (signed short *)write_buffer; + if (p_record_buffer_dir) + { + i = 0; + while(i < ii) + { + *s++ = audio_law_to_s32[*data++]; + *s++ = p_record_buffer[p_record_buffer_readp]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + } else + { + i = 0; + while(i < ii) + { + *s++ = p_record_buffer[p_record_buffer_readp]; + *s++ = audio_law_to_s32[*data++]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + i++; + } + } + fwrite(write_buffer, ii<<2, 1, p_record); + p_record_length += (ii<<2); + break; + + case CODEC_8BIT: + d = write_buffer; + i = 0; + while(i < ii) + { + sample = p_record_buffer[p_record_buffer_readp] + + audio_law_to_s32[*data++]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + if (sample < SHORT_MIN) sample = SHORT_MIN; + if (sample > SHORT_MAX) sample = SHORT_MAX; + *d++ = (sample+0x8000) >> 8; + i++; + } + fwrite(write_buffer, ii, 1, p_record); + p_record_length += ii; + break; + + case CODEC_LAW: + d = write_buffer; + i = 0; + while(i < ii) + { + sample = p_record_buffer[p_record_buffer_readp] + + audio_law_to_s32[*data++]; + p_record_buffer_readp = (p_record_buffer_readp + 1) & RECORD_BUFFER_MASK; + if (sample < SHORT_MIN) sample = SHORT_MIN; + if (sample > SHORT_MAX) sample = SHORT_MAX; + *d++ = audio_s16_to_law[sample & 0xffff]; + i++; + } + fwrite(write_buffer, ii, 1, p_record); + p_record_length += ii; + break; + } + length -= ii; + /* still data */ + if (length) + goto different_again; + /* no data (maybe buffer) */ + return; + +} +