[zaptel-commits] qwell: branch qwell/echocan-debug r2414 -
/team/qwell/echocan-debug/
zaptel-commits at lists.digium.com
zaptel-commits at lists.digium.com
Fri Apr 13 09:57:54 MST 2007
Author: qwell
Date: Fri Apr 13 11:57:54 2007
New Revision: 2414
URL: http://svn.digium.com/view/zaptel?view=rev&rev=2414
Log:
Add the ability in ztmonitor to monitor the audio before the echocan changed
it, as well as after it was changed. See the -p, -F, -R, and -T options.
Add the ability to ztmonitor to save rx and tx streams independently.
Add a bunch of examples for the new arguments in ztmonitor.
Modified ztmonitor to allow multiple options at once. Previously, you
couldn't save the file to disk and use -v at the same time.
The "both" pre-echocan option to ztmonitor was tested and works. The
independent rx/tx pre-echcan streams have not been tested yet.
Modified:
team/qwell/echocan-debug/zaptel-base.c
team/qwell/echocan-debug/zaptel.h
team/qwell/echocan-debug/ztmonitor.c
Modified: team/qwell/echocan-debug/zaptel-base.c
URL: http://svn.digium.com/view/zaptel/team/qwell/echocan-debug/zaptel-base.c?view=diff&rev=2414&r1=2413&r2=2414
==============================================================================
--- team/qwell/echocan-debug/zaptel-base.c (original)
+++ team/qwell/echocan-debug/zaptel-base.c Fri Apr 13 11:57:54 2007
@@ -1271,6 +1271,8 @@
chan->master = chan;
if (!chan->readchunk)
chan->readchunk = chan->sreadchunk;
+ if (!chan->readchunkpreec)
+ chan->readchunkpreec = NULL;
if (!chan->writechunk)
chan->writechunk = chan->swritechunk;
zt_set_law(chan, 0);
@@ -3962,6 +3964,11 @@
/* Get alias */
chans[i]->_confn = zt_get_conf_alias(stack.conf.confno);
}
+
+ if ((stack.conf.confmode & ZT_CONF_MODE_MASK) >= ZT_CONF_MONITOR_PREECHO && (stack.conf.confmode & ZT_CONF_MODE_MASK) <= ZT_CONF_MONITORBOTH_PREECHO) {
+ chans[stack.conf.confno]->readchunkpreec = kmalloc(sizeof(short) * ZT_MAX_CHUNKSIZE, GFP_KERNEL);
+ }
+
spin_unlock_irqrestore(&chan->lock, flags);
spin_unlock_irqrestore(&bigzaplock, flagso);
if (copy_to_user((struct zt_confinfo *) data,&stack.conf,sizeof(stack.conf)))
@@ -4982,6 +4989,39 @@
for (x=0;x<ZT_CHUNKSIZE;x++)
txb[x] = ZT_LIN2X(getlin[x], ms);
break;
+ case ZT_CONF_MONITOR_PREECHO: /* Monitor a channel's rx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(getlin, chans[ms->confna]->readchunkpreec);
+ } else {
+ ACSS(getlin, chans[ms->confna]->putlin);
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_MONITORTX_PREECHO: /* Monitor a channel's tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(getlin, chans[ms->confna]->putlin);
+ } else {
+ ACSS(getlin, chans[ms->confna]->readchunkpreec);
+ }
+
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_MONITORBOTH_PREECHO: /* monitor a channel's rx and tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ ACSS(getlin, chans[ms->confna]->putlin);
+ ACSS(getlin, chans[ms->confna]->readchunkpreec);
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
case ZT_CONF_REALANDPSEUDO:
/* This strange mode takes the transmit buffer and
puts it on the conference, minus its last sample,
@@ -5671,6 +5711,13 @@
int x;
unsigned long flags;
spin_lock_irqsave(&ss->lock, flags);
+ if (ss->readchunkpreec) {
+ /* Save a copy of the audio before the echo can has it's way with it */
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ /* We only ever really need to deal with signed linear - let's just convert it now */
+ ss->readchunkpreec[x] = ZT_XLAW(rxchunk[x], ss);
+ }
+ }
/* Perform echo cancellation on a chunk if necessary */
if (ss->ec) {
#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)
@@ -5901,6 +5948,45 @@
when you're so loud you're clipping anyway */
ACSS(putlin, chans[ms->confna]->getlin);
ACSS(putlin, chans[ms->confna]->putlin);
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITOR_PREECHO: /* Monitor a channel's rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(putlin, chans[ms->confna]->getlin);
+ } else {
+ ACSS(putlin, chans[ms->confna]->readchunkpreec);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITORTX_PREECHO: /* Monitor a channel's tx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(putlin, chans[ms->confna]->readchunkpreec);
+ } else {
+ ACSS(putlin, chans[ms->confna]->getlin);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITORBOTH_PREECHO: /* Monitor a channel's tx and rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Note: Technically, saturation should be done at
+ the end of the whole addition, but for performance
+ reasons, we don't do that. Besides, it only matters
+ when you're so loud you're clipping anyway */
+ ACSS(putlin, chans[ms->confna]->getlin);
+ ACSS(putlin, chans[ms->confna]->readchunkpreec);
/* Convert back */
for(x=0;x<ZT_CHUNKSIZE;x++)
rxb[x] = ZT_LIN2X(putlin[x], ms);
Modified: team/qwell/echocan-debug/zaptel.h
URL: http://svn.digium.com/view/zaptel/team/qwell/echocan-debug/zaptel.h?view=diff&rev=2414&r1=2413&r2=2414
==============================================================================
--- team/qwell/echocan-debug/zaptel.h (original)
+++ team/qwell/echocan-debug/zaptel.h Fri Apr 13 11:57:54 2007
@@ -970,6 +970,9 @@
#define ZT_CONF_CONFANNMON 7 /* conference announce/monitor mode */
#define ZT_CONF_REALANDPSEUDO 8 /* real and pseudo port both on conf */
#define ZT_CONF_DIGITALMON 9 /* Do not decode or interpret */
+#define ZT_CONF_MONITOR_PREECHO 10 /* monitor mode (rx of other chan) - before echo can is done */
+#define ZT_CONF_MONITORTX_PREECHO 11 /* monitor mode (tx of other chan) - before echo can is done */
+#define ZT_CONF_MONITORBOTH_PREECHO 12 /* monitor mode (rx & tx of other chan) - before echo can is done */
#define ZT_CONF_FLAG_MASK 0xff00 /* mask for flags */
#define ZT_CONF_LISTENER 0x100 /* is a listener on the conference */
#define ZT_CONF_TALKER 0x200 /* is a talker on the conference */
@@ -1129,6 +1132,7 @@
u_char swritechunk[ZT_MAX_CHUNKSIZE]; /* Buffer to be written */
u_char *readchunk; /* Actual place to read from */
u_char sreadchunk[ZT_MAX_CHUNKSIZE]; /* Preallocated static area */
+ short *readchunkpreec;
/* Pointer to tx and rx gain tables */
u_char *rxgain;
Modified: team/qwell/echocan-debug/ztmonitor.c
URL: http://svn.digium.com/view/zaptel/team/qwell/echocan-debug/ztmonitor.c?view=diff&rev=2414&r1=2413&r2=2414
==============================================================================
--- team/qwell/echocan-debug/ztmonitor.c (original)
+++ team/qwell/echocan-debug/ztmonitor.c Fri Apr 13 11:57:54 2007
@@ -51,11 +51,11 @@
#define FRAG_SIZE 8
-/* Put the ofh (output file handle) outside
+/* Put the ofh (output file handles) outside
* the main loop in case we ever add a signal
- * hanlder.
+ * handler.
*/
-static FILE* ofh = 0;
+static FILE* ofh[4] = {0, 0, 0, 0};
static int stereo = 0;
static int verbose = 0;
@@ -255,62 +255,142 @@
int main(int argc, char *argv[])
{
- int afd = -1, pfd, pfd2 = -1;
+ int afd = -1;
+ int pfd[4] = {-1, -1, -1, -1};
short buf[8192];
short buf2[16384];
char output_file[255];
int res, res2;
int visual = 0;
- int x,i;
+ int multichannel = 0;
+ int ossoutput = 0;
+ int preecho = 0;
+ int savefile = 0;
+ int x, i;
struct zt_confinfo zc;
if ((argc < 2) || (atoi(argv[1]) < 1)) {
- fprintf(stderr, "Usage: ztmonitor <channel num> [-v[v]] [-f FILE]\n");
+ fprintf(stderr, "Usage: ztmonitor <channel num> [-v[v]] [-m] [-o] [-p] [-f FILE | -r FILE1 -t FILE2] [-F FILE | -R FILE1 -T FILE2]\n");
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -v: Visual mode. Implies -m.\n");
+ fprintf(stderr, " -vv: Visual/Verbose mode. Implies -m.\n");
+ fprintf(stderr, " -m: Separate rx/tx streams.\n");
+ fprintf(stderr, " -o: Output audio via OSS. Note: Only 'normal' rx/tx streams are output via OSS.\n");
+ fprintf(stderr, " -p: Get a pre-echocanceled stream.\n");
+ fprintf(stderr, " -f FILE: Save combined rx/tx stream to FILE. Cannot be used with -m.\n");
+ fprintf(stderr, " -r FILE: Save rx stream to FILE. Implies -m.\n");
+ fprintf(stderr, " -t FILE: Save tx stream to FILE. Implies -m.\n");
+ fprintf(stderr, " -F FILE: Save combined pre-echocanceled rx/tx stream to FILE. Cannot be used with -m. Implies -p.\n");
+ fprintf(stderr, " -R FILE: Save pre-echocanceled rx stream to FILE. Implies -m and -p.\n");
+ fprintf(stderr, " -T FILE: Save pre-echocanceled tx stream to FILE. Implies -m and -p.\n");
+ fprintf(stderr, "Examples:\n");
+ fprintf(stderr, "Save a stream to a file\n");
+ fprintf(stderr, " ztmonitor 1 -f stream.raw\n");
+ fprintf(stderr, "Visualize an rx/tx stream and save them to separate files.\n");
+ fprintf(stderr, " ztmonitor 1 -v -r streamrx.raw -t streamtx.raw\n");
+ fprintf(stderr, "Play a combined rx/tx stream via OSS and save it to a file\n");
+ fprintf(stderr, " ztmonitor 1 -o -f stream.raw\n");
+ fprintf(stderr, "Play a combined rx/tx stream via OSS and save them to separate files\n");
+ fprintf(stderr, " ztmonitor 1 -m -o -r streamrx.raw -t streamtx.raw\n");
+ fprintf(stderr, "Save a combined normal rx/tx stream and a combined 'preecho' rx/tx stream to files\n");
+ fprintf(stderr, " ztmonitor 1 -m -p -f stream.raw -F streampreecho.raw\n");
+ fprintf(stderr, "Save a normal rx/tx stream and a 'preecho' rx/tx stream to separate files\n");
+ fprintf(stderr, " ztmonitor 1 -m -p -r streamrx.raw -t streamtx.raw -R streampreechorx.raw -T streampreechotx.raw\n");
exit(1);
}
for (i = 2; i < argc; ++i) {
if (!strcmp(argv[i], "-v")) {
- if (visual)
- verbose = 1;
+ if (visual)
+ verbose = 1;
visual = 1;
+ multichannel = 1;
} else if (!strcmp(argv[i], "-vv")) {
visual = 1;
verbose = 1;
- } else if (!strcmp(argv[i], "-f") && (i+1) < argc) {
- ++i; /*we care about hte file name */
+ multichannel = 1;
+ } else if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "-r") || !strcmp(argv[i], "-t")
+ || !strcmp(argv[i], "-F") || !strcmp(argv[i], "-R") || !strcmp(argv[i], "-T"))
+ && (i+1) < argc) {
+ /* Set which file descriptor to use */
+ if (!strcmp(argv[i], "-f")) {
+ savefile = 1;
+ x = 0;
+ } else if (!strcmp(argv[i], "-r")) {
+ savefile = 1;
+ multichannel = 1;
+ x = 0;
+ } else if (!strcmp(argv[i], "-t")) {
+ savefile = 1;
+ multichannel = 1;
+ x = 1;
+ } else if (!strcmp(argv[i], "-F")) {
+ savefile = 1;
+ preecho = 1;
+ x = 2;
+ } else if (!strcmp(argv[i], "-R")) {
+ savefile = 1;
+ multichannel = 1;
+ preecho = 1;
+ x = 2;
+ } else if (!strcmp(argv[i], "-T")) {
+ savefile = 1;
+ multichannel = 1;
+ preecho = 1;
+ x = 3;
+ } else
+ x = 0;
+
+ ++i; /* we care about the file name */
if (strlen(argv[i]) < 255 ) {
strcpy(output_file, argv[i]);
fprintf(stderr, "Output to %s\n", output_file);
- if ((ofh = fopen(output_file, "w"))<0) {
+ if ((ofh[x] = fopen(output_file, "w"))<0) {
fprintf(stderr, "Could not open %s for writing: %s\n", output_file, strerror(errno));
- exit(0);
+ exit(1);
}
- fprintf(stderr, "Run e.g., 'sox -r 8000 -s -w -c 1 file.raw file.wav' to convert.\n");
+ fprintf(stderr, "Run e.g., 'sox -r 8000 -s -w -c 1 %s file.wav' to convert.\n", output_file);
} else {
fprintf(stderr, "File Name %s too long\n",argv[i+1]);
}
- }
- }
- if (!visual) {
+ } else if (!strcmp(argv[i], "-m")) {
+ multichannel = 1;
+ } else if (!strcmp(argv[i], "-o")) {
+ ossoutput = 1;
+ } else if (!strcmp(argv[i], "-p")) {
+ preecho = 1;
+ }
+ }
+ if (ossoutput) {
/* Open audio */
if ((afd = audio_open()) < 0) {
printf("Cannot open audio ...\n");
- if (!ofh) exit(0);
- }
- }
+ ossoutput = 0;
+ }
+ }
+ if (!ossoutput && !multichannel && !savefile) {
+ fprintf(stderr, "Nothing to do with the stream(s) ...\n");
+ exit(1);
+ }
+
/* Open Pseudo device */
- if ((pfd = pseudo_open()) < 0)
+ if ((pfd[0] = pseudo_open()) < 0)
exit(1);
- if (visual && ((pfd2 = pseudo_open()) < 0))
+ if (multichannel && ((pfd[1] = pseudo_open()) < 0))
exit(1);
+ if (preecho) {
+ if ((pfd[2] = pseudo_open()) < 0)
+ exit(1);
+ if (multichannel && ((pfd[3] = pseudo_open()) < 0))
+ exit(1);
+ }
/* Conference them */
- memset(&zc, 0, sizeof(zc));
- zc.chan = 0;
- zc.confno = atoi(argv[1]);
- if (visual) {
+ if (multichannel) {
+ memset(&zc, 0, sizeof(zc));
+ zc.chan = 0;
+ zc.confno = atoi(argv[1]);
/* Two pseudo's, one for tx, one for rx */
zc.confmode = ZT_CONF_MONITORTX;
- if (ioctl(pfd, ZT_SETCONF, &zc) < 0) {
+ if (ioctl(pfd[0], ZT_SETCONF, &zc) < 0) {
fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
exit(1);
}
@@ -318,15 +398,47 @@
zc.chan = 0;
zc.confno = atoi(argv[1]);
zc.confmode = ZT_CONF_MONITOR;
- if (ioctl(pfd2, ZT_SETCONF, &zc) < 0) {
+ if (ioctl(pfd[1], ZT_SETCONF, &zc) < 0) {
fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
exit(1);
}
+ if (preecho) {
+ memset(&zc, 0, sizeof(zc));
+ zc.chan = 0;
+ zc.confno = atoi(argv[1]);
+ /* Two pseudo's, one for tx, one for rx */
+ zc.confmode = ZT_CONF_MONITORTX_PREECHO;
+ if (ioctl(pfd[2], ZT_SETCONF, &zc) < 0) {
+ fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(&zc, 0, sizeof(zc));
+ zc.chan = 0;
+ zc.confno = atoi(argv[1]);
+ zc.confmode = ZT_CONF_MONITOR_PREECHO;
+ if (ioctl(pfd[3], ZT_SETCONF, &zc) < 0) {
+ fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
} else {
+ memset(&zc, 0, sizeof(zc));
+ zc.chan = 0;
+ zc.confno = atoi(argv[1]);
zc.confmode = ZT_CONF_MONITORBOTH;
- if (ioctl(pfd, ZT_SETCONF, &zc) < 0) {
+ if (ioctl(pfd[0], ZT_SETCONF, &zc) < 0) {
fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
exit(1);
+ }
+ if (preecho) {
+ memset(&zc, 0, sizeof(zc));
+ zc.chan = 0;
+ zc.confno = atoi(argv[1]);
+ zc.confmode = ZT_CONF_MONITORBOTH_PREECHO;
+ if (ioctl(pfd[2], ZT_SETCONF, &zc) < 0) {
+ fprintf(stderr, "Unable to monitor: %s\n", strerror(errno));
+ exit(1);
+ }
}
}
if (visual) {
@@ -338,31 +450,64 @@
}
/* Now, copy from pseudo to audio */
for (;;) {
- res = read(pfd, buf, sizeof(buf));
- if (res < 1)
+ res = read(pfd[0], buf, sizeof(buf));
+ if (res < 1)
break;
- if (visual) {
- res2 = read(pfd2, buf2, res);
- if (res2 < 1)
+ if (ofh[0])
+ fwrite(buf, 1, res, ofh[0]);
+
+ if (multichannel) {
+ res2 = read(pfd[1], buf2, res);
+ if (res2 < 1)
break;
- if (res == res2)
- visualize((short *)buf, (short *)buf2, res/2);
- else
- printf("Huh? res = %d, res2 = %d?\n", res, res2);
-
- } else {
- if (ofh)
- fwrite(buf, 1, res, ofh);
- if (afd) {
- if (stereo) {
- for (x=0;x<res;x++)
- buf2[x<<1] = buf2[(x<<1) + 1] = buf[x];
- write(afd, buf2, res << 1);
- } else
- write(afd, buf, res);
+ if (ofh[1])
+ fwrite(buf2, 1, res2, ofh[1]);
+
+ if (visual) {
+ if (res == res2)
+ visualize((short *)buf, (short *)buf2, res/2);
+ else
+ printf("Huh? res = %d, res2 = %d?\n", res, res2);
}
}
- }
- if (ofh) fclose(ofh); /*Never Reached */
+
+ if (preecho) {
+ res = read(pfd[2], buf, sizeof(buf));
+ if (res < 1)
+ break;
+ if (ofh[2])
+ fwrite(buf, 1, res, ofh[2]);
+
+ if (multichannel) {
+ res2 = read(pfd[3], buf2, res);
+ if (res2 < 1)
+ break;
+ if (ofh[3])
+ fwrite(buf2, 1, res, ofh[3]);
+
+ /* XXX How are we going to visualize the preecho set of streams?
+ if (visual) {
+ if (res == res2)
+ visualize((short *)buf, (short *)buf2, res/2);
+ else
+ printf("Huh? res = %d, res2 = %d?\n", res, res2);
+ } */
+ }
+ }
+
+ /* XXX This probably doesn't work with multichannel
+ if (ossoutput && afd) {
+ if (stereo || multichannel) {
+ for (x=0;x<res;x++)
+ buf2[x<<1] = buf2[(x<<1) + 1] = buf[x];
+ write(afd, buf2, res << 1);
+ } else
+ write(afd, buf, res);
+ } */
+ }
+ if (ofh[0]) fclose(ofh[0]);
+ if (ofh[1]) fclose(ofh[1]);
+ if (ofh[2]) fclose(ofh[2]);
+ if (ofh[3]) fclose(ofh[3]);
exit(0);
}
More information about the zaptel-commits
mailing list