From b47cebd49fc3a595df943c06d2cb3dc0e76733f6 Mon Sep 17 00:00:00 2001 From: Morten Rolland Date: Sun, 28 Oct 2001 19:02:03 +0000 Subject: [PATCH] Try to establish a more generic low level soundcard/ISDN/winmodem hardware abstraction layer at the most fundamental level. --- amodemd.c | 2 +- include/ifax/misc/hardware-driver.h | 4 +- misc/isdnline.c | 31 +++-- misc/statemachine.c | 2 +- modules/linedriver.c | 176 ++++++++++++++-------------- 5 files changed, 109 insertions(+), 106 deletions(-) diff --git a/amodemd.c b/amodemd.c index e752f60..9401a0b 100644 --- a/amodemd.c +++ b/amodemd.c @@ -197,7 +197,7 @@ void main(int ac, char **av) mh = modem_initialize(); linedriver = ifax_create_module(IFAX_LINEDRIVER); - ifax_command(linedriver,CMD_LINEDRIVER_ISDN,hh); + ifax_command(linedriver,CMD_LINEDRIVER_HARDWARE,hh); /* ifax_command(linedriver,CMD_LINEDRIVER_AUDIO); */ /* ifax_command(linedriver,CMD_LINEDRIVER_RECORD,"modem.dat"); */ diff --git a/include/ifax/misc/hardware-driver.h b/include/ifax/misc/hardware-driver.h index ce037f6..c939c79 100644 --- a/include/ifax/misc/hardware-driver.h +++ b/include/ifax/misc/hardware-driver.h @@ -98,11 +98,11 @@ struct HardwareHandle { /* Read a number of samples from the hardware driver. */ - void (*read)(struct HardwareHandle *hh, ifax_uint8 *samples); + void (*read)(struct HardwareHandle *hh, ifax_uint8 *smpls, int cnt); /* Write a number of (16-bit signed) samples to the hardware driver. */ - void (*write)(struct HardwareHandle *hh, ifax_sint16 *samples); + void (*write)(struct HardwareHandle *hh, ifax_sint16 *smpls, int cnt); /* Supply the hardware driver with configuration parameters. */ diff --git a/misc/isdnline.c b/misc/isdnline.c index a1561ec..e26e722 100644 --- a/misc/isdnline.c +++ b/misc/isdnline.c @@ -34,6 +34,8 @@ * and work with the hardware abstraction layer in 'hardware-driver.c' */ +#define _GNU_SOURCE + #define FSM_DEBUG_STATES #include @@ -178,27 +180,33 @@ static void isdn_service_select(struct HardwareHandle *hh, fd_set *rfd, } -/* Write a set of voice samples to the ISDN device. The samples are in the +/* + * Write a set of voice samples to the ISDN device. The samples are in the * form of an array, containing 16-bit signed, linear values. * The array is *modified* by this function to the linear values actually * transmitted. This is to help an echo cancel function to work on the true * values as sent over the ISDN line to the remote end. - * - * The number of samples that needs to be sent is dictated by the value - * in the hardware driver handle: hh->write_size. This function will assume - * there are exactly this many samples in the 'src' array. */ -static void isdn_write_samples(struct HardwareHandle *hh, ifax_sint16 *src) +#define ISDN_MAX_WRITE 4096 + +static void isdn_write_samples(struct HardwareHandle *hh, + ifax_sint16 *src, int cnt) { struct IsdnHandle *ih = hh->private; ifax_uint8 *dst, wala; ifax_uint16 linear; - int t; - - dst = &ih->tmp_write[0]; + ifax_uint8 tmp[2 * ISDN_MAX_WRITE]; - for ( t=0; t < hh->write_size; t++ ) { + if ( cnt > ISDN_MAX_WRITE ) { + ifax_dprintf(DEBUG_ERROR,"Dropping %d samples in isdn_write_samples", + cnt - ISDN_MAX_WRITE); + cnt = ISDN_MAX_WRITE; + } + + dst = &tmp[0]; + + while ( cnt-- > 0 ) { linear = (ifax_uint16) *src; wala = sint2wala[linear>>4]; @@ -209,8 +217,7 @@ static void isdn_write_samples(struct HardwareHandle *hh, ifax_sint16 *src) *dst++ = wala; } - iobuffer_fill(ih->outgoing_buffer,&ih->tmp_write[0], - dst - &ih->tmp_write[0]); + iobuffer_fill(ih->outgoing_buffer, &tmp[0], dst - &tmp[0]); } diff --git a/misc/statemachine.c b/misc/statemachine.c index b83bf23..c752859 100644 --- a/misc/statemachine.c +++ b/misc/statemachine.c @@ -47,7 +47,7 @@ * */ -#include +#include #include #include diff --git a/modules/linedriver.c b/modules/linedriver.c index 7142be7..c5d96ec 100644 --- a/modules/linedriver.c +++ b/modules/linedriver.c @@ -26,19 +26,8 @@ ****************************************************************************** */ -/* NOTE: This code deals with timing-critical device-driver - * buffering with synchronized read/write on same - * device and between devices (ISDN/Audio). - * The relationships between buffer-sizes, read/write - * strategy and possible timing glitches should be - * investigated more closely. UPDATE: Should work well - * as long as (low-level) transmit-buffer is - * pre-charged to avoid draining. - * - * BUGS: Error situations are not handeled well (no warnings etc.) - */ - -/* Maintain buffers and send/receive samples to the underlying +/* + * Maintain buffers and send/receive samples to the underlying * physical transmission medium, like ISDN or a soundcard, named * pipes for faxing self (debugging) etc. * @@ -83,7 +72,8 @@ #include #include -/* These defines should probably be run-time configurable. +/* + * These defines should probably be run-time configurable. * BUFFERSIZE is the size of the main output buffer which is * filled by the handle function (incomming signal-chain). * We need a buffer here since the signaling-chain may not generate @@ -97,7 +87,8 @@ #define MAXIOSIZE 256 -/* An instance of this linedriver module holds the following +/* + * An instance of this linedriver module holds the following * state-information and buffer storage. */ @@ -122,7 +113,8 @@ typedef struct { } linedriver_private; -/* Method of operation: +/* + * Method of operation: * * The 'handle' function is called, possibly as a result of a 'demand' * request, with data to fill the output queue (TX). However, no data @@ -154,97 +146,101 @@ static int linedriver_handle(ifax_modp self, void *data, size_t length) static int work(ifax_modp self) { - int wanted, chunk, total, more, t; - ifax_sint32 sp; - ifax_uint32 up; - linedriver_private *priv = self->private; + int wanted, chunk, total, more, t; + ifax_sint32 sp; + ifax_uint32 up; + linedriver_private *priv = self->private; + struct HardwareHandle *hh; - /* When driving the ISDN-line, this is how it works: - * Read as much as we can from the line, and then - * transmit *exactly* as much. This will keep the - * flow of samples smooth. It may be a very good idea - * to pre-charge the output-buffers somewhat to be more - * resistant to buffer-drains during heavy computations. - */ + /* When driving the ISDN-line, this is how it works: + * Read as much as we can from the line, and then + * transmit *exactly* as much. This will keep the + * flow of samples smooth. It may be a very good idea + * to pre-charge the output-buffers somewhat to be more + * resistant to buffer-drains during heavy computations. + */ - total = more = 0; + total = more = 0; - do { + do { - chunk = MAXIOSIZE; /* Default buffer-size of IO-operations */ + chunk = MAXIOSIZE; /* Default size of IO-operations */ - if ( priv->ih != 0 ) { - /* ISDN is online, read first, then transmit */ - chunk = IsdnReadSamples(priv->ih,&priv->rx_buffer[0],MAXIOSIZE); - if ( chunk < 0 ) - return -1; - more = chunk == MAXIOSIZE; - } else - for ( t=0; t < chunk; t++) - priv->rx_buffer[t] = 0; + if ( priv->hh != 0 && priv->hh->state == ONLINE ) { + /* Hardware is online, read first, then transmit */ + hh = priv->hh; + chunk = hh->read(hh,&priv->rx_buffer[0],MAXIOSIZE); + if ( chunk < 0 ) + return -1; + more = chunk == MAXIOSIZE; + } else { + for ( t=0; t < chunk; t++) + priv->rx_buffer[t] = 0; + } + - /* Fill output queue by demanding data (if needed) */ - while ( priv->output.size < chunk ) { - wanted = chunk - priv->output.size + 5; - ifax_handle_demand (self->recvfrom, wanted); - } + /* Fill output queue by demanding data (if needed) */ + while ( priv->output.size < chunk ) { + wanted = chunk - priv->output.size + 5; + ifax_handle_demand (self->recvfrom, wanted); + } - /* Prepare TX-buffer */ - priv->output.size -= chunk; - for (t = 0; t < chunk; t++) { - priv->tx_buffer[t] = priv->output.buffer[priv->output.rp++]; - if ( priv->output.rp >= BUFFERSIZE ) - priv->output.rp = 0; - } + /* Prepare TX-buffer */ + priv->output.size -= chunk; + for (t = 0; t < chunk; t++) { + priv->tx_buffer[t] = priv->output.buffer[priv->output.rp++]; + if ( priv->output.rp >= BUFFERSIZE ) + priv->output.rp = 0; + } - if ( priv->ih != 0 ) { - /* ISDN is online, send TX-buffer */ - IsdnWriteSamples(priv->ih,&priv->tx_buffer[0],chunk); - } + if ( priv->ih != 0 ) { + /* ISDN is online, send TX-buffer */ + IsdnWriteSamples(priv->ih,&priv->tx_buffer[0],chunk); + } - if ( priv->loopback ) { - /* Software loopback enabled, overwrite receive-buffer */ - for ( t=0; t < chunk; t++ ) - priv->rx_buffer[t] = priv->tx_buffer[t]; - } + if ( priv->loopback ) { + /* Software loopback enabled, overwrite receive-buffer */ + for ( t=0; t < chunk; t++ ) + priv->rx_buffer[t] = priv->tx_buffer[t]; + } - if ( priv->dsp_fd >= 0 ) { - /* Soundcard audio monitoring enabled */ - for ( t=0; t < chunk; t++ ) { - sp = priv->tx_buffer[t] * priv->dsp_tx_volume; - up = sp; - up >>= 16; - priv->stereo_buffer[t+t+0] = up; - - sp = priv->rx_buffer[t] * priv->dsp_rx_volume; - up = sp; - up >>= 16; - priv->stereo_buffer[t+t+1] = up; - } + if ( priv->dsp_fd >= 0 ) { + /* Soundcard audio monitoring enabled */ + for ( t=0; t < chunk; t++ ) { + sp = priv->tx_buffer[t] * priv->dsp_tx_volume; + up = sp; + up >>= 16; + priv->stereo_buffer[t+t+0] = up; - write(priv->dsp_fd, priv->stereo_buffer, 4*chunk); - } + sp = priv->rx_buffer[t] * priv->dsp_rx_volume; + up = sp; + up >>= 16; + priv->stereo_buffer[t+t+1] = up; + } - if ( priv->rec_fd >= 0 ) { - /* Recording all audio to file */ - for ( t=0; t < chunk; t++ ) { - priv->stereo_buffer[t+t+0] = priv->tx_buffer[t]; - priv->stereo_buffer[t+t+1] = priv->rx_buffer[t]; - } + write(priv->dsp_fd, priv->stereo_buffer, 4*chunk); + } - write(priv->rec_fd, priv->stereo_buffer, 4*chunk); - } + if ( priv->rec_fd >= 0 ) { + /* Recording all audio to file */ + for ( t=0; t < chunk; t++ ) { + priv->stereo_buffer[t+t+0] = priv->tx_buffer[t]; + priv->stereo_buffer[t+t+1] = priv->rx_buffer[t]; + } - if ( self->sendto != 0 ) { - /* Feed the receiver signaling-chain */ - ifax_handle_input(self->sendto, &priv->rx_buffer[0], chunk); - } + write(priv->rec_fd, priv->stereo_buffer, 4*chunk); + } - total += chunk; + if ( self->sendto != 0 ) { + /* Feed the receiver signaling-chain */ + ifax_handle_input(self->sendto, &priv->rx_buffer[0], chunk); + } - } while ( more ); + total += chunk; - return total; + } while ( more ); + + return total; } static void linedriver_destroy(ifax_modp self)