[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