467 lines
14 KiB
C
467 lines
14 KiB
C
/*
|
|
* functions for mediation between ISDN and OSS
|
|
*
|
|
* This file is part of ANT (Ant is Not a Telephone)
|
|
*
|
|
* Copyright 2002, 2003 Roland Stigge
|
|
*
|
|
* ANT is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* ANT is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with ANT; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*
|
|
*
|
|
* NOTE:
|
|
* * for performance reasons, separate recording buffers are filled while
|
|
* mediating
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
/* regular GNU system includes */
|
|
#include <stdio.h>
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <math.h>
|
|
|
|
/* own header files */
|
|
#include "globals.h"
|
|
#include "session.h"
|
|
/* ulaw conversion (LUT) */
|
|
#include "g711.h"
|
|
#include "isdn.h"
|
|
#include "sound.h"
|
|
#include "util.h"
|
|
#include "mediation.h"
|
|
#include "llcheck.h"
|
|
#include "fxgenerator.h"
|
|
#include "recording.h"
|
|
|
|
/*
|
|
* allocate memory and build look-up-tables for audio <-> isdn conversion
|
|
*
|
|
* input:
|
|
* format_in, LUT_in: used audio format and pointer to look-up-table
|
|
* for conversion of isdn -> audio
|
|
* format_out, LUT_out: the same for audio -> isdn
|
|
* LUT_generate: table for conversion of 8 bit unsigned -> isdn
|
|
* LUT_analyze: table for conversion of isdn -> 8 bit unsigned
|
|
*
|
|
* return: 0 on success, -1 otherwise
|
|
*
|
|
* NOTE: the caller has to free the memory of LUT_* itself
|
|
*/
|
|
int mediation_makeLUT(int format_in, unsigned char **LUT_in,
|
|
int format_out, unsigned char **LUT_out,
|
|
unsigned char **LUT_generate,
|
|
unsigned char **LUT_analyze,
|
|
short **LUT_ulaw2short) {
|
|
int sample_size_in;
|
|
int sample_size_out;
|
|
int buf_size_in;
|
|
int buf_size_out;
|
|
int sample;
|
|
int i;
|
|
short s;
|
|
|
|
/* Allocation */
|
|
sample_size_in = sample_size_from_format(format_in); /* isdn -> audio */
|
|
if (sample_size_in == 0 ||
|
|
!(*LUT_in = (unsigned char *)malloc(buf_size_in = sample_size_in * 256)))
|
|
return -1;
|
|
|
|
sample_size_out = sample_size_from_format(format_out); /* audio -> isdn */
|
|
if (sample_size_out == 0 ||
|
|
!(*LUT_out =
|
|
(unsigned char *)malloc(buf_size_out =
|
|
(1 + (sample_size_out - 1) * 255) * 256)))
|
|
return -1;
|
|
|
|
if (!(*LUT_generate = (unsigned char*) malloc (256)))
|
|
return -1;
|
|
if (!(*LUT_analyze = (unsigned char*) malloc (256)))
|
|
return -1;
|
|
if (!(*LUT_ulaw2short = (short*) malloc (256*sizeof(short))))
|
|
return -1;
|
|
|
|
/* Calculation */
|
|
for (i = 0; i < buf_size_in; i += sample_size_in) { /* isdn -> audio */
|
|
switch(format_in) {
|
|
case AFMT_U8:
|
|
(*LUT_in)[i] = (unsigned char)((ulaw2linear((unsigned char)i) / 256 &
|
|
0xff) ^ 0x80);
|
|
break;
|
|
|
|
case AFMT_S8:
|
|
(*LUT_in)[i] = (unsigned char)(ulaw2linear((unsigned char)i) / 256 &
|
|
0xff);
|
|
break;
|
|
|
|
case AFMT_MU_LAW:
|
|
(*LUT_in)[i] = (unsigned char)i;
|
|
break;
|
|
|
|
case AFMT_S16_LE:
|
|
sample = ulaw2linear((unsigned char)(i / 2));
|
|
(*LUT_in)[i] = (unsigned char)(sample & 0xff);
|
|
(*LUT_in)[i+1] = (unsigned char)(sample >> 8 & 0xff);
|
|
break;
|
|
|
|
case AFMT_S16_BE:
|
|
sample = ulaw2linear((unsigned char)(i / 2));
|
|
(*LUT_in)[i+1] = (unsigned char)(sample & 0xff);
|
|
(*LUT_in)[i] = (unsigned char)(sample >> 8 & 0xff);
|
|
break;
|
|
|
|
case AFMT_U16_LE:
|
|
sample = ulaw2linear((unsigned char)(i / 2));
|
|
(*LUT_in)[i] = (unsigned char)(sample & 0xff);
|
|
(*LUT_in)[i+1] = (unsigned char)((sample >> 8 & 0xff) ^ 0x80);
|
|
break;
|
|
|
|
case AFMT_U16_BE:
|
|
sample = ulaw2linear((unsigned char)(i / 2));
|
|
(*LUT_in)[i+1] = (unsigned char)(sample & 0xff);
|
|
(*LUT_in)[i] = (unsigned char)((sample >> 8 & 0xff) ^ 0x80);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,
|
|
"Error: "
|
|
"Unsupported format appeared while building input LUT.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < buf_size_out; i++) { /* audio -> isdn */
|
|
switch(format_out) {
|
|
case AFMT_U8:
|
|
(*LUT_out)[i] = linear2ulaw((i - 128) * 256);
|
|
break;
|
|
|
|
case AFMT_S8:
|
|
(*LUT_out)[i] = linear2ulaw(i * 256);
|
|
break;
|
|
|
|
case AFMT_MU_LAW:
|
|
(*LUT_out)[i] = (unsigned char)i;
|
|
break;
|
|
|
|
/* next 4 cases:
|
|
input int i stores first buffer byte in low byte */
|
|
case AFMT_S16_LE:
|
|
(*LUT_out)[i] = linear2ulaw((int)(signed char)(i >> 8) << 8 |
|
|
(int)(i & 0xff));
|
|
break;
|
|
|
|
case AFMT_S16_BE:
|
|
(*LUT_out)[i] = linear2ulaw((int)(signed char)(i & 0xff) << 8 |
|
|
(int)(i >> 8));
|
|
break;
|
|
|
|
case AFMT_U16_LE:
|
|
(*LUT_out)[i] = linear2ulaw(i - 32768);
|
|
break;
|
|
|
|
case AFMT_U16_BE:
|
|
(*LUT_out)[i] = linear2ulaw(((i & 0xff) << 8 | i >> 8) - 32768);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,
|
|
"Error: "
|
|
"Unsupported format appeared while building output LUT.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 256; i++) { /* 8 bit unsigned -> isdn -> 8 bit unsigned */
|
|
(*LUT_generate)[i] = linear2ulaw((i - 128) * 256);
|
|
(*LUT_ulaw2short)[i] = s = ulaw2linear((unsigned char)i); /* ulaw->short */
|
|
(*LUT_analyze)[i] = (unsigned char)((s / 256 & 0xff) ^ 0x80);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* writes buffer carefully out to file (ttyI / audio device)
|
|
*
|
|
* returns 0 on success, -1 otherwise (write error)
|
|
*/
|
|
int write_buf(int fd, unsigned char *outbuf, int outbuf_size) {
|
|
int towrite = outbuf_size;
|
|
int written = 0;
|
|
|
|
/* write until everything has been written */
|
|
while (towrite && (written != -1 || errno == EAGAIN)) {
|
|
written = write(fd, &outbuf[outbuf_size - towrite], towrite);
|
|
if (debug >= 2)
|
|
fprintf(stderr, "Wrote %d bytes to device.\n", written);
|
|
if (written != -1)
|
|
towrite -= written;
|
|
else
|
|
if (errno == EAGAIN) {
|
|
if (debug)
|
|
fprintf(stderr, "write_buf: EAGAIN\n");
|
|
ant_sleep(SHORT_INTERVAL);
|
|
}
|
|
}
|
|
if (written == -1) {
|
|
perror("write_buf");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* XXX: smooth samples when converting speeds in next 2 functions */
|
|
|
|
/*
|
|
* process isdn input from ttyI to sound device
|
|
*
|
|
* to be called after select found block to read in isdn file descriptor
|
|
*/
|
|
void process_isdn_source(session_t *session) {
|
|
int got, i, j;
|
|
unsigned char inbyte; /* byte read from ttyI */
|
|
int to_process; /* number of samples to process
|
|
(according to ratio / ratio_support_count) */
|
|
unsigned char sample; /* 8 bit unsigned sample */
|
|
int max = 0; /* for llcheck */
|
|
|
|
short s; /* libsndfile sample data */
|
|
|
|
got = read(session->isdn_fd, session->isdn_inbuf,
|
|
session->isdn_inbuf_size);
|
|
|
|
if (debug >= 2)
|
|
fprintf(stderr, "From isdn: got %d bytes.\n", got);
|
|
|
|
if (got != -1) {
|
|
for (i = 0; i < got; i++) {
|
|
inbyte = session->isdn_inbuf[i];
|
|
if (!session->escape == (inbyte != DLE)) {
|
|
/* normal mode or last byte was an escape in DLE mode */
|
|
|
|
/* input line level check */
|
|
sample = session->audio_LUT_analyze[inbyte];
|
|
if (abs((int)sample - 128) > max)
|
|
max = abs((int)sample - 128);
|
|
|
|
/* recording */
|
|
if (session->option_record) {
|
|
if (session->option_record_remote)
|
|
s = session->audio_LUT_ulaw2short[inbyte];
|
|
else
|
|
s = 0;
|
|
session->rec_buf_remote[session->rec_buf_remote_index++] = s;
|
|
if (session->rec_buf_remote_index >= session->rec_buf_remote_size) {
|
|
if (recording_write(session->recorder, session->rec_buf_remote,
|
|
session->rec_buf_remote_size, RECORDING_REMOTE))
|
|
fprintf(stderr, "Warning: Recording (remote) error.\n");
|
|
session->rec_buf_remote_index = 0;
|
|
}
|
|
}
|
|
|
|
/* touchtone to audio: after llcheck to monitor other end */
|
|
if (session->touchtone_countdown_audio > 0) {
|
|
inbyte = fxgenerate(session, EFFECT_TOUCHTONE,
|
|
session->touchtone_index,
|
|
(double)session->touchtone_countdown_audio /
|
|
ISDN_SPEED); /* playing reverse is ok */
|
|
session->touchtone_countdown_audio--;
|
|
}
|
|
|
|
/* mediation */
|
|
to_process = (int)floor((double)(session->samples_in + 1) *
|
|
session->ratio_in) -
|
|
(int)floor((double)session->samples_in *
|
|
session->ratio_in);
|
|
/* printf("isdn -> audio: to_process == %d\n", to_process); */
|
|
for (j = 0; j < to_process; j++) {
|
|
if (session->audio_sample_size_out == 1) {
|
|
session->audio_outbuf[session->audio_outbuf_index++] =
|
|
session->audio_LUT_in[(int)inbyte];
|
|
} else { /* audio_sample_size == 2 */
|
|
session->audio_outbuf[session->audio_outbuf_index++] =
|
|
session->audio_LUT_in[(int)inbyte * 2];
|
|
session->audio_outbuf[session->audio_outbuf_index++] =
|
|
session->audio_LUT_in[(int)inbyte * 2 + 1];
|
|
}
|
|
if (session->audio_outbuf_index >= session->fragment_size_out) {
|
|
if (write_buf(session->audio_fd_out, session->audio_outbuf,
|
|
session->fragment_size_out))
|
|
session->aborted = 1;
|
|
session->audio_outbuf_index = 0;
|
|
}
|
|
}
|
|
|
|
session->samples_in++;
|
|
|
|
if (session->escape) {
|
|
session->escape = 0;
|
|
if (debug) fprintf(stderr, "debug: escape mode off after 2x DLE.\n");
|
|
}
|
|
} else if (!session->escape && inbyte == DLE) { /* new escape: DLE */
|
|
session->escape = 1;
|
|
if (debug)
|
|
fprintf(stderr, "debug: ttyI DLE escape mode on.\n");
|
|
} else /* i.e. if (*escape) */ {
|
|
if (inbyte == DC4 || inbyte == ETX) {
|
|
session->hangup = 1;
|
|
} else {/* else: must be a touchtone: ignored */
|
|
if (debug) {
|
|
if ((inbyte >= '0' && inbyte <= '9') || inbyte == '#' ||
|
|
inbyte =='*' || (inbyte >= 'A' && inbyte <= 'D'))
|
|
fprintf(stderr, "Touchtone %c received.\n", inbyte);
|
|
else
|
|
fprintf(stderr, "Warning: Unknown escape sequence received.\n");
|
|
}
|
|
}
|
|
|
|
session->escape = 0;
|
|
if (debug) fprintf(stderr,
|
|
"debug: escape mode off after special char.\n");
|
|
}
|
|
}
|
|
|
|
llcheck_bar_set(session->llcheck_in, (double)max / 128);
|
|
|
|
} else {
|
|
fprintf(stderr, "process_isdn_source: read error (return -1).\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* process audio input from sound device to isdn tty
|
|
*
|
|
* to be called after select found fragment(s) to read from
|
|
*/
|
|
void process_audio_source(session_t *session) {
|
|
int i, j, got, n;
|
|
unsigned char sample; /* the ulaw sample */
|
|
short s = 0; /* libsndfile sample data */
|
|
/* the ulaw sample when muted: */
|
|
unsigned char zero = session->audio_LUT_generate[128];
|
|
int to_process; /* number of samples to process
|
|
(according to ratio / ratio_support_count) */
|
|
unsigned char sampleu8; /* 8 bit unsigned sample */
|
|
int max = 0; /* for llcheck */
|
|
|
|
got = read(session->audio_fd_in, session->audio_inbuf,
|
|
session->fragment_size_in);
|
|
|
|
if (debug >= 2)
|
|
fprintf(stderr, "From audio: got %d bytes.\n", got);
|
|
|
|
if (got != -1) {
|
|
for (i = 0; i < got;
|
|
i += session->audio_sample_size_in, session->samples_out++) {
|
|
|
|
to_process = (int)floor((double)(session->samples_out + 1)
|
|
* session->ratio_out) -
|
|
(int)floor((double)session->samples_out
|
|
* session->ratio_out);
|
|
/* printf("audio -> isdn: to_process == %d\n", to_process); */
|
|
for (j = 0; j < to_process; j++) {
|
|
if (session->audio_sample_size_in == 1) {
|
|
sample = session->audio_LUT_out[(int)(session->audio_inbuf[i])];
|
|
} else { /* audio_sample_size == 2 */
|
|
/* multiple byte samples are used "little endian" in int
|
|
to look up in LUT (see mediation_makeLUT) */
|
|
sample = session->audio_LUT_out[(int)(session->audio_inbuf[i]) |
|
|
(int)(session->audio_inbuf[i+1])
|
|
<< 8];
|
|
}
|
|
|
|
/* touchtone to isdn: before llcheck to monitor it */
|
|
if (session->touchtone_countdown_isdn > 0) {
|
|
sample = fxgenerate(session, EFFECT_TOUCHTONE,
|
|
session->touchtone_index,
|
|
(double)session->touchtone_countdown_isdn /
|
|
ISDN_SPEED /* playing reverse is ok */ );
|
|
session->touchtone_countdown_isdn--;
|
|
}
|
|
|
|
if (session->option_muted) /* zero if muted */
|
|
sample = zero;
|
|
|
|
/* input line level check */
|
|
sampleu8 = session->audio_LUT_analyze[sample];
|
|
if (abs((int)sampleu8 - 128) > max)
|
|
max = abs((int)sampleu8 - 128);
|
|
|
|
/* recording */
|
|
if (session->option_record) {
|
|
if (session->option_record_local)
|
|
s = session->audio_LUT_ulaw2short[sample];
|
|
else
|
|
s = 0;
|
|
session->rec_buf_local[session->rec_buf_local_index++] = s;
|
|
if (session->rec_buf_local_index >= session->rec_buf_local_size) {
|
|
if (recording_write(session->recorder, session->rec_buf_local,
|
|
session->rec_buf_local_size, RECORDING_LOCAL))
|
|
fprintf(stderr, "Warning: Recording (local) error.\n");
|
|
session->rec_buf_local_index = 0;
|
|
}
|
|
}
|
|
|
|
n = (sample == DLE) ? 2 : 1; /* again if DLE escape */
|
|
while (n > 0) {
|
|
session->isdn_outbuf[session->isdn_outbuf_index++] = sample;
|
|
if (session->isdn_outbuf_index >= session->isdn_outbuf_size) {
|
|
/* write outbuf out */
|
|
if (write_buf(session->isdn_fd, session->isdn_outbuf,
|
|
session->isdn_outbuf_size))
|
|
session->aborted = 1;
|
|
session->isdn_outbuf_index = 0;
|
|
}
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
|
|
llcheck_bar_set(session->llcheck_out, (double)max / 128);
|
|
|
|
} else {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
if (debug)
|
|
fprintf(stderr,
|
|
"process_audio_source: "
|
|
"EAGAIN - no data immediately available (that's ok).\n");
|
|
break;
|
|
case EBADF:
|
|
fprintf(stderr,
|
|
"process_audio_source: EBADF - invalid file descriptor.\n");
|
|
break;
|
|
case EINTR:
|
|
fprintf(stderr,
|
|
"process_audio_source: EINTR - interrupted by signal.\n");
|
|
break;
|
|
case EIO:
|
|
fprintf(stderr,
|
|
"process_audio_source: EIO - hardware error.\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|