diff --git a/CHANGES.md b/CHANGES.md
index 92975bf3..182fe64d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,10 @@
     >
     > Add:     "FX25TX 1" (or 16 or 32 or 64)
 
+- stdout is now supported for audio output via piping to other utilities.  To support this, all non-audio output must be redirected to stderr using the new -O option on the command line.
+
+- udp audio output is also now supported.  Use udp:destination:port style output device in the configuration file.
+
 
 
 ### Bugs Fixed: ###
diff --git a/conf/generic.conf b/conf/generic.conf
index d9f7b355..631824dc 100644
--- a/conf/generic.conf
+++ b/conf/generic.conf
@@ -132,10 +132,15 @@
 %W%# "stdin" is not an audio device.  Don't use this unless you
 %W%# understand what this means.  Read the User Guide.
 %W%# You can also specify "UDP:" and an optional port for input.
-%W%# Something different must be specified for output.
+%W%# "-" or "stdout" can be used to pipe audio out to another application.
+%W%# The -O option must be specified on the command line to support this.
+%W%# For UDP output, specify the destination IP address/hostname and port number
+%W%# using "UDP:destination:port" syntax
 %W%
 %W%# ADEVICE stdin 0
 %W%# ADEVICE UDP:7355 0
+%W%# ADEVICE UDP:7355 UDP:localhost:7356
+%W%# ADEVICE stdin stdout
 %W%
 %W%# The position in the list can change when devices (e.g. USB) are added and removed.
 %W%# You can also specify devices by using part of the name.
@@ -158,10 +163,15 @@
 %L%# "stdin" is not an audio device.  Don't use this unless you
 %L%# understand what this means.  Read the User Guide.
 %L%# You can also specify "UDP:" and an optional port for input.
-%L%# Something different must be specified for output.
+%L%# "-" or "stdout" can be used to pipe audio out to another application.
+%L%# The -O option must be specified on the command line to support this.
+%L%# For UDP output, specify the destination IP address/hostname and port number
+%L%# using "UDP:destination:port" syntax
 %L%
 %L%# ADEVICE stdin plughw:1,0
 %L%# ADEVICE UDP:7355 default
+%L%# ADEVICE UDP:7355 UDP:localhost:7356
+%L%# ADEVICE stdin stdout
 %L%
 %R% ---------- Mac ----------
 %R%
@@ -183,9 +193,14 @@
 %M%# "stdin" is not an audio device.  Don't use this unless you
 %M%# understand what this means.  Read the User Guide.
 %M%# You can also specify "UDP:" and an optional port for input.
-%M%# Something different must be specified for output.
+%M%# "-" or "stdout" can be used to pipe audio out to another application.
+%M%# The -O option must be specified on the command line to support this.
+%M%# For UDP output, specify the destination IP address/hostname and port number
+%M%# using "UDP:destination:port" syntax
 %M%
 %M%# ADEVICE UDP:7355 default
+%M%# ADEVICE UDP:7355 UDP:localhost:7356
+%M%# ADEVICE stdin stdout
 %M%#
 %C%
 %C%#
@@ -607,4 +622,4 @@
 %C%#TTERR  NO_CALL          SPEECH  No call or object name.
 %C%#TTERR  SATSQ            SPEECH  Satellite square must be 4 digits.
 %C%#TTERR  SUFFIX_NO_CALL   SPEECH  Send full call before using suffix.
-%C%
\ No newline at end of file
+%C%
diff --git a/man/direwolf.1 b/man/direwolf.1
index 93f786dc..aee9ee2b 100644
--- a/man/direwolf.1
+++ b/man/direwolf.1
@@ -155,6 +155,9 @@ x = Silence FX.25 information.
 .BI "-t " "n"
 Text colors.  0=disabled. 1=default.  2,3,4,... alternatives.  Use 9 to test compatibility with your terminal.
 
+.TP
+.BI "-O "
+Redirects all printed output to stderr so stdout can be used as audio device.
 
 .TP
 .B "-p " 
diff --git a/src/aprs_tt.c b/src/aprs_tt.c
index 7b125759..f8c8edb3 100644
--- a/src/aprs_tt.c
+++ b/src/aprs_tt.c
@@ -2078,6 +2078,7 @@ static void check_result (void)
 
 int main (int argc, char *argv[])
 {
+	text_color_init (1, 0);
 	aprs_tt_init (NULL, 0);
 
 	error_count = 0;
diff --git a/src/atest.c b/src/atest.c
index c5f4ec50..ae24e8bd 100644
--- a/src/atest.c
+++ b/src/atest.c
@@ -217,7 +217,7 @@ int main (int argc, char *argv[])
 	}
 #endif
 
-	text_color_init(1);
+	text_color_init(1, 0);
 	text_color_set(DW_COLOR_INFO);
 
 /* 
diff --git a/src/audio.c b/src/audio.c
index 82dec22a..737247ad 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -75,6 +75,7 @@
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netdb.h>
 #include <errno.h>
 
 
@@ -129,8 +130,11 @@ static struct adev_s {
 	int outbuf_len;
 
 	enum audio_in_type_e g_audio_in_type;
+	enum audio_out_type_e g_audio_out_type;
 
-	int udp_sock;			/* UDP socket for receiving data */
+	int udp_in_sock;			/* UDP socket for receiving data */
+	int udp_out_sock;			/* UDP socket for sending data */
+	struct sockaddr_storage udp_dest_addr;	/* Destination address for UDP socket sending */
 
 } adev[MAX_ADEVS];
 
@@ -222,8 +226,8 @@ int audio_open (struct audio_s *pa)
 #endif
 	int chan;
 	int a;
-	char audio_in_name[30];
-	char audio_out_name[30];
+	char audio_in_name[80];
+	char audio_out_name[80];
 
 
 	save_audio_config_p = pa;
@@ -238,7 +242,7 @@ int audio_open (struct audio_s *pa)
 #else
 	  adev[a].oss_audio_device_fd = -1;
 #endif
-	  adev[a].udp_sock = -1;
+	  adev[a].udp_in_sock = -1;
 	}
 
 
@@ -412,7 +416,7 @@ int audio_open (struct audio_s *pa)
 	          //int data_size = 0;
 
 	          //Create UDP Socket
-	          if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
+	          if ((adev[a].udp_in_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
 	            text_color_set(DW_COLOR_ERROR);
 	            dw_printf ("Couldn't create socket, errno %d\n", errno);
 	            return -1;
@@ -424,7 +428,7 @@ int audio_open (struct audio_s *pa)
 	          si_me.sin_addr.s_addr = htonl(INADDR_ANY);
 
 	          //Bind to the socket
-	          if (bind(adev[a].udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
+	          if (bind(adev[a].udp_in_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
 	            text_color_set(DW_COLOR_ERROR);
 	            dw_printf ("Couldn't bind socket, errno %d\n", errno);
 	            return -1;
@@ -453,52 +457,134 @@ int audio_open (struct audio_s *pa)
   	    }
 
 /*
- * Output device.  Only "soundcard" is supported at this time. 
+ * Output device.  Soundcard, stdout, and UDP are supported at this time.
  */
+	    if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
+	      adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
+	      if (!dw_printf_redirected()) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("stdout must only be used with the -O option\n");
+	        return (-1);
+	      }
+	    } else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
+	      adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
+	      /* User must supply address and port */
+	      if (strcasecmp(pa->adev[a].adevice_out,"udp:") == 0 ||
+	          strlen(pa->adev[a].adevice_out) < 7 ||
+	          strstr(pa->adev[a].adevice_out+5, ":") == 0) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("Destination address and port must be supplied for UDP output\n");
+	        return (-1);
+	      }
+	    } else {
+	      adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
+	    }
 
+	    switch (adev[a].g_audio_out_type) {
+	      case AUDIO_OUT_TYPE_STDOUT:
+	        adev[a].outbuf_size_in_bytes = 1024;
+	        break;
+
+	      case AUDIO_OUT_TYPE_SOUNDCARD:
 #if USE_ALSA
-	    err = snd_pcm_open (&(adev[a].audio_out_handle), audio_out_name, SND_PCM_STREAM_PLAYBACK, 0);
+	        err = snd_pcm_open (&(adev[a].audio_out_handle), audio_out_name, SND_PCM_STREAM_PLAYBACK, 0);
 
-	    if (err < 0) {
-	      text_color_set(DW_COLOR_ERROR);
-	      dw_printf ("Could not open audio device %s for output\n%s\n", 
-			audio_out_name, snd_strerror(err));
-	      if (err == -EBUSY) {
-	        dw_printf ("This means that some other application is using that device.\n");
-	        dw_printf ("The solution is to identify that other application and stop it.\n");
-	      }
-	      return (-1);
-	    }
+	        if (err < 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Could not open audio device %s for output\n%s\n",
+	          audio_out_name, snd_strerror(err));
+	          if (err == -EBUSY) {
+	            dw_printf ("This means that some other application is using that device.\n");
+	            dw_printf ("The solution is to identify that other application and stop it.\n");
+	          }
+	          return (-1);
+	        }
 
-	    adev[a].outbuf_size_in_bytes = set_alsa_params (a, adev[a].audio_out_handle, pa, audio_out_name, "output");
+	        adev[a].outbuf_size_in_bytes = set_alsa_params (a, adev[a].audio_out_handle, pa, audio_out_name, "output");
 
-	    if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) {
-	      return (-1);
-	    }
+	        if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) {
+	          return (-1);
+	        }
 
 #elif USE_SNDIO
-	    adev[a].sndio_out_handle = sio_open (audio_out_name, SIO_PLAY, 0);
-	    if (adev[a].sndio_out_handle == NULL) {
-	      text_color_set(DW_COLOR_ERROR);
-	      dw_printf ("Could not open audio device %s for output\n",
-			audio_out_name);
-	      return (-1);
-	    }
+	        adev[a].sndio_out_handle = sio_open (audio_out_name, SIO_PLAY, 0);
+	        if (adev[a].sndio_out_handle == NULL) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Could not open audio device %s for output\n",
+	          audio_out_name);
+	          return (-1);
+	        }
 
-	    adev[a].outbuf_size_in_bytes = set_sndio_params (a, adev[a].sndio_out_handle, pa, audio_out_name, "output");
+	        adev[a].outbuf_size_in_bytes = set_sndio_params (a, adev[a].sndio_out_handle, pa, audio_out_name, "output");
 
-	    if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) {
-	      return (-1);
-	    }
+	        if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) {
+	          return (-1);
+	        }
 
-	    if (!sio_start (adev[a].sndio_out_handle)) {
-	      text_color_set(DW_COLOR_ERROR);
-	      dw_printf ("Could not start audio device %s for output\n",
-			audio_out_name);
-	      return (-1);
-	    }
+	        if (!sio_start (adev[a].sndio_out_handle)) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Could not start audio device %s for output\n",
+	          audio_out_name);
+	          return (-1);
+	        }
 #endif
+		break;
+
+	      case AUDIO_OUT_TYPE_SDR_UDP:;
+
+	        struct addrinfo ai_out;
+	        struct addrinfo *ai_res;
+	        char udp_outhost[256];
+	        char *udp_outport;
+	        int res;
+
+	        // Initialize structure for addrinfo restrictions
+	        memset((char *) &ai_out, 0, sizeof(ai_out));
+	        ai_out.ai_socktype = SOCK_DGRAM;
+	        ai_out.ai_protocol = IPPROTO_UDP;
+
+	        // Separate out the host and port strings
+	        strncpy(udp_outhost, pa->adev[a].adevice_out+4, 255);
+	        udp_outhost[255] = 0;
+	        udp_outport = strstr(udp_outhost,":");
+	        *udp_outport++ = 0;
+
+	        if (strlen(udp_outport) == 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("UDP output destination port must be supplied\n");
+	          return -1;
+	        }
+
+	        // Get the sockaddr to represent the host/port provided
+	        res = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
+	        if (res != 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Error parsing/resolving UDP output address\n");
+	          return -1;
+	        }
+
+	        // IPv4 and IPv6 structs are different sizes
+	        if (ai_res->ai_family == AF_INET6) {
+	          res = sizeof(struct sockaddr_in6);
+	        } else {
+	           res = sizeof(struct sockaddr_in);
+	        }
+
+	        //Create UDP Socket for the right address family
+	        if ((adev[a].udp_out_sock=socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Couldn't create socket, errno %d\n", errno);
+	          return -1;
+	        }
 
+	        // Save sockaddr needed later to send the data and set buffer size
+	        memcpy(&adev[a].udp_dest_addr, ai_res->ai_addr, res);
+	        adev[a].outbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN;
+	        freeaddrinfo(ai_res);
+
+	        break;
+
+	    }
 /*
  * Finally allocate buffer for each direction.
  */
@@ -1205,8 +1291,8 @@ int audio_get (int a)
 	    while (adev[a].inbuf_next >= adev[a].inbuf_len) {
 	      int res;
 
-              assert (adev[a].udp_sock > 0);
-	      res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
+              assert (adev[a].udp_in_sock > 0);
+	      res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
 	      if (res < 0) {
 	        text_color_set(DW_COLOR_ERROR);
 	        dw_printf ("Can't read from udp socket, res=%d", res);
@@ -1333,13 +1419,37 @@ int audio_put (int a, int c)
 
 int audio_flush (int a)
 {
+	int res;
+	unsigned char *ptr;
+	int len;
+
+	switch (adev[a].g_audio_out_type) {
+	  case AUDIO_OUT_TYPE_STDOUT:;
+
+	    ptr = adev[a].outbuf_ptr;
+	    len = adev[a].outbuf_len;
+
+	    while (len > 0) {
+	      res = write(STDOUT_FILENO, ptr, (size_t) len);
+	      if (res <= 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf ("\nError writing to stdout.  Exiting.\n");
+	        exit (0);
+	      }
+	      ptr += res;
+	      len -= res;
+	    }
+	    adev[a].outbuf_len = 0;
+	    return 0;
+
+	  case AUDIO_OUT_TYPE_SOUNDCARD:;
 #if USE_ALSA
-	int k;
-	unsigned char *psound;
-	int retries = 10;
-	snd_pcm_status_t *status;
+	    int k;
+	    unsigned char *psound;
+	    int retries = 10;
+	    snd_pcm_status_t *status;
 
-	assert (adev[a].audio_out_handle != NULL);
+	    assert (adev[a].audio_out_handle != NULL);
 
 
 /*
@@ -1352,159 +1462,186 @@ int audio_flush (int a)
  */
 
 
-	snd_pcm_status_alloca(&status);
+	    snd_pcm_status_alloca(&status);
 
-	k = snd_pcm_status (adev[a].audio_out_handle, status);
-	if (k != 0) {
-	  text_color_set(DW_COLOR_ERROR);
-	  dw_printf ("Audio output get status error.\n%s\n", snd_strerror(k));
-	}
+	    k = snd_pcm_status (adev[a].audio_out_handle, status);
+	    if (k != 0) {
+	      text_color_set(DW_COLOR_ERROR);
+	      dw_printf ("Audio output get status error.\n%s\n", snd_strerror(k));
+	    }
 
-	if ((k = snd_pcm_status_get_state(status)) != SND_PCM_STATE_RUNNING) {
+	    if ((k = snd_pcm_status_get_state(status)) != SND_PCM_STATE_RUNNING) {
 
-	  //text_color_set(DW_COLOR_DEBUG);
-	  //dw_printf ("Audio output state = %d.  Try to start.\n", k);
+	      //text_color_set(DW_COLOR_DEBUG);
+	      //dw_printf ("Audio output state = %d.  Try to start.\n", k);
 
-	  k = snd_pcm_prepare (adev[a].audio_out_handle);
+	      k = snd_pcm_prepare (adev[a].audio_out_handle);
 
-	  if (k != 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("Audio output start error.\n%s\n", snd_strerror(k));
-	  }
-	}
+	      if (k != 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio output start error.\n%s\n", snd_strerror(k));
+	      }
+	    }
 
 
-	psound = adev[a].outbuf_ptr;
+	    psound = adev[a].outbuf_ptr;
 
-	while (retries-- > 0) {
+	    while (retries-- > 0) {
 
-	  k = snd_pcm_writei (adev[a].audio_out_handle, psound, adev[a].outbuf_len / adev[a].bytes_per_frame);	
+	      k = snd_pcm_writei (adev[a].audio_out_handle, psound, adev[a].outbuf_len / adev[a].bytes_per_frame);
 #if DEBUGx
-	  text_color_set(DW_COLOR_DEBUG);
-	  dw_printf ("audio_flush(): snd_pcm_writei %d frames returns %d\n",
-				adev[a].outbuf_len / adev[a].bytes_per_frame, k);
-	  fflush (stdout);	
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("audio_flush(): snd_pcm_writei %d frames returns %d\n",
+	      adev[a].outbuf_len / adev[a].bytes_per_frame, k);
+	      fflush (stdout);
 #endif
-	  if (k == -EPIPE) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("Audio output data underrun.\n");
+	      if (k == -EPIPE) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio output data underrun.\n");
 
-	    /* No problemo.  Recover and go around again. */
+	        /* No problemo.  Recover and go around again. */
 
-	    snd_pcm_recover (adev[a].audio_out_handle, k, 1);
-	  }
-          else if (k == -ESTRPIPE) {
-            text_color_set(DW_COLOR_ERROR);
-            dw_printf ("Driver suspended, recovering\n");
-            snd_pcm_recover(adev[a].audio_out_handle, k, 1);
-          }
-          else if (k == -EBADFD) {
-            k = snd_pcm_prepare (adev[a].audio_out_handle);
-            if(k < 0) {
-              dw_printf ("Error preparing after bad state: %s\n", snd_strerror(k));
-            }
-          }
- 	  else if (k < 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("Audio write error: %s\n", snd_strerror(k));
+	        snd_pcm_recover (adev[a].audio_out_handle, k, 1);
+	      }
+	      else if (k == -ESTRPIPE) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Driver suspended, recovering\n");
+	        snd_pcm_recover(adev[a].audio_out_handle, k, 1);
+	      }
+	      else if (k == -EBADFD) {
+	        k = snd_pcm_prepare (adev[a].audio_out_handle);
+	        if(k < 0) {
+	          dw_printf ("Error preparing after bad state: %s\n", snd_strerror(k));
+	        }
+	      }
+	      else if (k < 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio write error: %s\n", snd_strerror(k));
 
-	    /* Some other error condition. */
-	    /* Try again. What do we have to lose? */
+	        /* Some other error condition. */
+	        /* Try again. What do we have to lose? */
 
-            k = snd_pcm_prepare (adev[a].audio_out_handle);
-            if(k < 0) {
-              dw_printf ("Error preparing after error: %s\n", snd_strerror(k));
-            }
-	  }
- 	  else if (k != adev[a].outbuf_len / adev[a].bytes_per_frame) {
-	    text_color_set(DW_COLOR_ERROR);
-	    dw_printf ("Audio write took %d frames rather than %d.\n",
- 			k, adev[a].outbuf_len / adev[a].bytes_per_frame);
-	
-	    /* Go around again with the rest of it. */
+	        k = snd_pcm_prepare (adev[a].audio_out_handle);
+	        if(k < 0) {
+	          dw_printf ("Error preparing after error: %s\n", snd_strerror(k));
+	        }
+	      }
+	      else if (k != adev[a].outbuf_len / adev[a].bytes_per_frame) {
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Audio write took %d frames rather than %d.\n",
+	        k, adev[a].outbuf_len / adev[a].bytes_per_frame);
 
-	    psound += k * adev[a].bytes_per_frame;
-	    adev[a].outbuf_len -= k * adev[a].bytes_per_frame;
-	  }
-	  else {
-	    /* Success! */
-	    adev[a].outbuf_len = 0;
-	    return (0);
-	  }
-	}
+	        /* Go around again with the rest of it. */
+
+	        psound += k * adev[a].bytes_per_frame;
+	        adev[a].outbuf_len -= k * adev[a].bytes_per_frame;
+	      }
+	      else {
+	        /* Success! */
+	        adev[a].outbuf_len = 0;
+	        return (0);
+	      }
+	    }
 
-	text_color_set(DW_COLOR_ERROR);
-	dw_printf ("Audio write error retry count exceeded.\n");
+	    text_color_set(DW_COLOR_ERROR);
+	    dw_printf ("Audio write error retry count exceeded.\n");
 
-	adev[a].outbuf_len = 0;
-	return (-1);
+	    adev[a].outbuf_len = 0;
+	    return (-1);
 
 #elif USE_SNDIO
 
-	int k;
-	unsigned char *ptr;
-	int len;
+	    int k;
+	    unsigned char *ptr;
+	    int len;
 
-	ptr = adev[a].outbuf_ptr;
-	len = adev[a].outbuf_len;
+	    ptr = adev[a].outbuf_ptr;
+	    len = adev[a].outbuf_len;
 
-	while (len > 0) {
-	  assert (adev[a].sndio_out_handle != NULL);
-	  if (poll_sndio (adev[a].sndio_out_handle, POLLOUT) < 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    perror("Can't write to audio device");
-	    adev[a].outbuf_len = 0;
-	    return (-1);
-	  }
+	    while (len > 0) {
+	      assert (adev[a].sndio_out_handle != NULL);
+	      if (poll_sndio (adev[a].sndio_out_handle, POLLOUT) < 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        perror("Can't write to audio device");
+	        adev[a].outbuf_len = 0;
+	        return (-1);
+	      }
 
-	  k = sio_write (adev[a].sndio_out_handle, ptr, len);
+	      k = sio_write (adev[a].sndio_out_handle, ptr, len);
 #if DEBUGx
-	  text_color_set(DW_COLOR_DEBUG);
-	  dw_printf ("audio_flush(): write %d returns %d\n", len, k);
-	  fflush (stdout);
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("audio_flush(): write %d returns %d\n", len, k);
+	      fflush (stdout);
 #endif
-	  ptr += k;
-	  len -= k;
-	}
+	      ptr += k;
+	      len -= k;
+	    }
 
-	adev[a].outbuf_len = 0;
-	return (0);
+	    adev[a].outbuf_len = 0;
+	    return (0);
 
 #else		/* OSS */
 
-	int k;
-	unsigned char *ptr;	
-	int len;
+	    int k;
+	    unsigned char *ptr;
+	    int len;
 
-	ptr = adev[a].outbuf_ptr;
-	len = adev[a].outbuf_len;
+	    ptr = adev[a].outbuf_ptr;
+	    len = adev[a].outbuf_len;
 
-	while (len > 0) {
-	  assert (adev[a].oss_audio_device_fd > 0);
-	  k = write (adev[a].oss_audio_device_fd, ptr, len);
+	    while (len > 0) {
+	      assert (adev[a].oss_audio_device_fd > 0);
+	      k = write (adev[a].oss_audio_device_fd, ptr, len);
 #if DEBUGx
-	  text_color_set(DW_COLOR_DEBUG);
-	  dw_printf ("audio_flush(): write %d returns %d\n", len, k);
-	  fflush (stdout);	
+	      text_color_set(DW_COLOR_DEBUG);
+	      dw_printf ("audio_flush(): write %d returns %d\n", len, k);
+	      fflush (stdout);
 #endif
-	  if (k < 0) {
-	    text_color_set(DW_COLOR_ERROR);
-	    perror("Can't write to audio device");
-	    adev[a].outbuf_len = 0;
-	    return (-1);
-	  }
-	  if (k < len) {
-	    /* presumably full but didn't block. */
-	    usleep (10000);
-	  }
-	  ptr += k;
-	  len -= k;
-	}
+	      if (k < 0) {
+	        text_color_set(DW_COLOR_ERROR);
+	        perror("Can't write to audio device");
+	        adev[a].outbuf_len = 0;
+	        return (-1);
+	      }
+	      if (k < len) {
+	        /* presumably full but didn't block. */
+	        usleep (10000);
+	      }
+	      ptr += k;
+	      len -= k;
+	    }
 
-	adev[a].outbuf_len = 0;
-	return (0);
+	    adev[a].outbuf_len = 0;
+	    return (0);
 #endif
 
+	  case AUDIO_OUT_TYPE_SDR_UDP:;
+
+	    ptr = adev[a].outbuf_ptr;
+	    len = adev[a].outbuf_len;
+
+	    while (len > 0) {
+
+	      assert (adev[a].udp_out_sock > 0);
+
+	      res = sendto(adev[a].udp_out_sock, adev[a].outbuf_ptr, len, 0, (struct sockaddr *)&adev[a].udp_dest_addr, sizeof(struct sockaddr_storage));
+
+	      if (res <= 0) {
+	        text_color_set(DW_COLOR_INFO);
+	        dw_printf ("\nError %d writing to UDP socket.  Exiting.\n", errno);
+	        exit (0);
+	      }
+
+	      ptr += res;
+	      len -= res;
+
+	    }
+
+	    adev[a].outbuf_len = 0;
+	    return 0;
+
+	}
+	return (0);
 } /* end audio_flush */
 
 
@@ -1546,9 +1683,12 @@ int audio_flush (int a)
 
 void audio_wait (int a)
 {	
-
 	audio_flush (a);
 
+	if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
+	  return;
+	}
+
 #if USE_ALSA
 
 	/* For playback, this should wait for all pending frames */
diff --git a/src/audio.h b/src/audio.h
index cb5ca94e..dc8444e4 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -43,6 +43,11 @@ enum audio_in_type_e {
 	AUDIO_IN_TYPE_SDR_UDP,
 	AUDIO_IN_TYPE_STDIN };
 
+enum audio_out_type_e {
+	AUDIO_OUT_TYPE_SOUNDCARD,
+	AUDIO_OUT_TYPE_SDR_UDP,
+	AUDIO_OUT_TYPE_STDOUT };
+
 /* For option to try fixing frames with bad CRC. */
 
 typedef enum retry_e {
diff --git a/src/audio_win.c b/src/audio_win.c
index 85a1548b..adba65ee 100644
--- a/src/audio_win.c
+++ b/src/audio_win.c
@@ -133,8 +133,8 @@ static int calcbufsize(int rate, int chans, int bits)
 
 static struct adev_s {
 
-	enum audio_in_type_e g_audio_in_type;	
-
+	enum audio_in_type_e g_audio_in_type;
+	enum audio_out_type_e g_audio_out_type;
 /*
  * UDP socket for receiving audio stream.
  * Buffer, length, and pointer for UDP or stdin.
@@ -146,6 +146,14 @@ static struct adev_s {
 	int stream_len;
 	int stream_next;
 
+/*
+ * UDP socket for transmitting audio stream.
+ * Buffer and index for stdout or UDP.
+ */
+	SOCKET udp_out_sock;
+	char stream_out_data[SDR_UDP_BUF_MAXLEN];
+	int stream_out_next;
+	struct sockaddr_storage udp_dest_addr;
 
 /* For sound output. */
 /* out_wavehdr.dwUser is used to keep track of output buffer state. */
@@ -286,6 +294,7 @@ int audio_open (struct audio_s *pa)
 
 
 	    A->udp_sock = INVALID_SOCKET;
+	    A->udp_out_sock = INVALID_SOCKET;
 
 	    in_dev_no[a] = WAVE_MAPPER;	/* = ((UINT)-1) in mmsystem.h */
 	    out_dev_no[a] = WAVE_MAPPER;
@@ -343,28 +352,50 @@ int audio_open (struct audio_s *pa)
 
 /*
  * Select output device.
- * Only soundcard at this point.
- * Purhaps we'd like to add UDP for an SDR transmitter.
+ * Soundcard, UDP, and stdout supported.
  */
-	    if (strlen(pa->adev[a].adevice_out) == 1 && isdigit(pa->adev[a].adevice_out[0])) {
-	      out_dev_no[a] = atoi(pa->adev[a].adevice_out);
-	    }
-	    else if (strlen(pa->adev[a].adevice_out) == 2 && isdigit(pa->adev[a].adevice_out[0]) && isdigit(pa->adev[a].adevice_out[1])) {
-	      out_dev_no[a] = atoi(pa->adev[a].adevice_out);
-	    }
+	    if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
+	      A->g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
+	      if (!dw_printf_redirected()) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("stdout must only be used with the -O option\n");
+	        return (-1);
+	      }
+	      /* Change - to stdout for readability. */
+	      strlcpy (pa->adev[a].adevice_out, "stdout", sizeof(pa->adev[a].adevice_out));
+	    } else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
+	      A->g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
+	      // User must supply address and port
+	      if (strcasecmp(pa->adev[a].adevice_out, "udp:") == 0 ||
+	          strlen(pa->adev[a].adevice_out) < 7 ||
+		  strstr(pa->adev[a].adevice_out+5, ":") == 0) {
+	        text_color_set (DW_COLOR_ERROR);
+		dw_printf ("Destination address and port must be supplied for UDP output\n");
+		return (-1);
+	      }
+	    } else {
+	      A->g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
 
-	    if ((UINT)(out_dev_no[a]) == WAVE_MAPPER && strlen(pa->adev[a].adevice_out) >= 1) {
-	      num_devices = waveOutGetNumDevs();
-	      for (n=0 ; n<num_devices && (UINT)(out_dev_no[a]) == WAVE_MAPPER ; n++) {
-	        if ( ! waveOutGetDevCaps(n, &woc, sizeof(WAVEOUTCAPS))) {
-	          if (strstr(woc.szPname, pa->adev[a].adevice_out) != NULL) {
-	            out_dev_no[a] = n;
+	      if (strlen(pa->adev[a].adevice_out) == 1 && isdigit(pa->adev[a].adevice_out[0])) {
+	        out_dev_no[a] = atoi(pa->adev[a].adevice_out);
+	      }
+	      else if (strlen(pa->adev[a].adevice_out) == 2 && isdigit(pa->adev[a].adevice_out[0]) && isdigit(pa->adev[a].adevice_out[1])) {
+	        out_dev_no[a] = atoi(pa->adev[a].adevice_out);
+	      }
+
+	      if ((UINT)(out_dev_no[a]) == WAVE_MAPPER && strlen(pa->adev[a].adevice_out) >= 1) {
+	        num_devices = waveOutGetNumDevs();
+	        for (n=0 ; n<num_devices && (UINT)(out_dev_no[a]) == WAVE_MAPPER ; n++) {
+	          if ( ! waveOutGetDevCaps(n, &woc, sizeof(WAVEOUTCAPS))) {
+	            if (strstr(woc.szPname, pa->adev[a].adevice_out) != NULL) {
+	              out_dev_no[a] = n;
+	            }
 	          }
 	        }
-	      }
-	      if ((UINT)(out_dev_no[a]) == WAVE_MAPPER) {
-	        text_color_set(DW_COLOR_ERROR);
-	        dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adev[a].adevice_out);
+	        if ((UINT)(out_dev_no[a]) == WAVE_MAPPER) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adev[a].adevice_out);
+	        }
 	      }
 	    }
 	  }   /* if defined */
@@ -424,7 +455,7 @@ int audio_open (struct audio_s *pa)
 
             struct adev_s *A = &(adev[a]);
 
-	    /* Display stdin or udp:port if appropriate. */   
+	    /* Display stdin or udp:port if appropriate. */
 
 	    if (A->g_audio_in_type != AUDIO_IN_TYPE_SOUNDCARD) {
 
@@ -498,6 +529,36 @@ int audio_open (struct audio_s *pa)
 	  }
 	}
 
+// Add UDP or stdout to end of device list if used.
+
+	for (a=0; a<MAX_ADEVS; a++) {
+	  if (pa->adev[a].defined) {
+
+	    struct adev_s *A = &(adev[a]);
+
+	    /* Display stdout or udp:port if appropriate. */
+
+	    if (A->g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
+
+	      int aaa;
+	      for (aaa=0; aaa<MAX_ADEVS; aaa++) {
+	        if (pa->adev[aaa].defined) {
+	          dw_printf (" %c", a == aaa ? '*' : ' ');
+
+	        }
+	      }
+	      dw_printf ("  %s                             ", pa->adev[a].adevice_out);	/* should be UDP:nnnn or stdout */
+
+	      if (pa->adev[a].num_channels == 2) {
+	        dw_printf ("   (channels %d & %d)", ADEVFIRSTCHAN(a), ADEVFIRSTCHAN(a)+1);
+	      }
+	      else {
+	        dw_printf ("   (channel %d)", ADEVFIRSTCHAN(a));
+	      }
+	      dw_printf ("\n");
+	    }
+	  }
+	}
 
 /*
  * Open for each audio device input/output pair.
@@ -523,32 +584,112 @@ int audio_open (struct audio_s *pa)
 
 /*
  * Open the audio output device.
- * Soundcard is only possibility at this time.
+ * Soundcard and stdout are only possibility at this time.
  */
 
-	     err = waveOutOpen (&(A->audio_out_handle), out_dev_no[a], &wf, (DWORD_PTR)out_callback, a, CALLBACK_FUNCTION);
-	     if (err != MMSYSERR_NOERROR) {
-	       text_color_set(DW_COLOR_ERROR);
-	       dw_printf ("Could not open audio device for output.\n");
-	       return (-1);
-	     }
-	  
+	    switch (A->g_audio_out_type) {
+
+	      case AUDIO_OUT_TYPE_SOUNDCARD:
 
+	        err = waveOutOpen (&(A->audio_out_handle), out_dev_no[a], &wf, (DWORD_PTR)out_callback, a, CALLBACK_FUNCTION);
+	          if (err != MMSYSERR_NOERROR) {
+	            text_color_set(DW_COLOR_ERROR);
+	            dw_printf ("Could not open audio device for output.\n");
+	            return (-1);
+	          }
+	        break;
 /*
  * Set up the output buffers.
  * We use dwUser to indicate it is available for filling.
  */
 
-	     memset ((void*)(A->out_wavehdr), 0, sizeof(A->out_wavehdr));
+	        memset ((void*)(A->out_wavehdr), 0, sizeof(A->out_wavehdr));
 
-	     for (n = 0; n < NUM_OUT_BUF; n++) {
-	       A->out_wavehdr[n].lpData = malloc(A->outbuf_size);
-	       A->out_wavehdr[n].dwUser = DWU_FILLING;	
-	       A->out_wavehdr[n].dwBufferLength = 0;
-	     }
-	     A->out_current = 0;			
+	        for (n = 0; n < NUM_OUT_BUF; n++) {
+	          A->out_wavehdr[n].lpData = malloc(A->outbuf_size);
+	          A->out_wavehdr[n].dwUser = DWU_FILLING;
+	          A->out_wavehdr[n].dwBufferLength = 0;
+	        }
+	        A->out_current = 0;
 
+	      case AUDIO_OUT_TYPE_SDR_UDP:;
 	
+	        WSADATA wsadata;
+	        struct addrinfo ai_out;
+	        struct addrinfo *ai_res;
+	        char udp_outhost[256];
+	        char *udp_outport;
+	        int err, res;
+
+	        err = WSAStartup (MAKEWORD(2,2), &wsadata);
+	        if (err != 0) {
+	            text_color_set(DW_COLOR_ERROR);
+	            dw_printf("WSAStartup failed: %d\n", err);
+	            return (-1);
+	        }
+
+	        if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
+	          text_color_set(DW_COLOR_ERROR);
+                  dw_printf("Could not find a usable version of Winsock.dll\n");
+                  WSACleanup();
+                  return (-1);
+	        }
+
+	        memset((char *) &ai_out, 0, sizeof(ai_out));
+	        ai_out.ai_socktype = SOCK_DGRAM;
+	        ai_out.ai_protocol = IPPROTO_UDP;
+
+	        strncpy(udp_outhost, pa->adev[a].adevice_out + 4, 255);
+	        udp_outhost[255] = 0;
+	        udp_outport = strstr(udp_outhost, ":");
+	        *udp_outport++ = 0;
+
+	        if (strlen(udp_outport) == 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf("UDP output destination port must be supplied\n");
+	          return -1;
+	        }
+
+	        err = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
+	        if (err != 0) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf("Error parsing/resolving UDP output address\n");
+	          return -1;
+	        }
+
+	        if (ai_res->ai_family == AF_INET6) {
+	          res = sizeof(struct sockaddr_in6);
+	        } else {
+	          res = sizeof(struct sockaddr_in);
+	        }
+
+	        // Create UDP Socket
+
+	        A->udp_out_sock = socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+	        if (A->udp_out_sock == INVALID_SOCKET) {
+	          text_color_set(DW_COLOR_ERROR);
+	          dw_printf ("Couldn't create socket, errno %d\n", WSAGetLastError());
+	          return -1;
+	        }
+
+	        memcpy(&A->udp_dest_addr, ai_res->ai_addr, res);
+	        A->stream_out_next = 0;
+
+	        break;
+
+	      case AUDIO_OUT_TYPE_STDOUT:
+
+	        setmode (STDOUT_FILENO, _O_BINARY);
+	        A->stream_out_next= 0;
+	        break;
+
+	      default:
+
+	        text_color_set(DW_COLOR_ERROR);
+	        dw_printf ("Internal error, invalid audio_out_type\n");
+	        return (-1);
+	    }
+
 /*
  * Open audio input device.
  * More possibilities here:  soundcard, UDP port, stdin.
@@ -942,49 +1083,69 @@ int audio_put (int a, int c)
 
 	struct adev_s *A;
 	A = &(adev[a]); 
-	
+
+	switch (A->g_audio_out_type) {
+
+	  case AUDIO_OUT_TYPE_SOUNDCARD:
+
 /* 
  * Wait if no buffers are available.
  * Don't use p yet because compiler might might consider dwFlags a loop invariant. 
  */
 
-	int timeout = 10;
-	while ( A->out_wavehdr[A->out_current].dwUser == DWU_PLAYING) {
-	  SLEEP_MS (ONE_BUF_TIME);
-	  timeout--;
-	  if (timeout <= 0) {
-	    text_color_set(DW_COLOR_ERROR);
+	    int timeout = 10;
+	    while ( A->out_wavehdr[A->out_current].dwUser == DWU_PLAYING) {
+	      SLEEP_MS (ONE_BUF_TIME);
+	      timeout--;
+	      if (timeout <= 0) {
+	        text_color_set(DW_COLOR_ERROR);
 
 // TODO: open issues 78 & 165.  How can we avoid/improve this?
 
-	    dw_printf ("Audio output failure waiting for buffer.\n");
-	    dw_printf ("This can occur when we are producing audio output for\n");
-	    dw_printf ("transmit and the operating system doesn't provide buffer\n");
-	    dw_printf ("space after waiting and retrying many times.\n");
-	    //dw_printf ("In recent years, this has been reported only when running the\n");
-	    //dw_printf ("Windows version with VMWare on a Macintosh.\n");
-	    ptt_term ();
-	    return (-1);
-	  }
-	}
+	        dw_printf ("Audio output failure waiting for buffer.\n");
+	        dw_printf ("This can occur when we are producing audio output for\n");
+	        dw_printf ("transmit and the operating system doesn't provide buffer\n");
+	        dw_printf ("space after waiting and retrying many times.\n");
+	        //dw_printf ("In recent years, this has been reported only when running the\n");
+	        //dw_printf ("Windows version with VMWare on a Macintosh.\n");
+	        ptt_term ();
+	        return (-1);
+	      }
+	    }
 
-	p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
+	    p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
+
+	    if (p->dwUser == DWU_DONE) {
+	      waveOutUnprepareHeader (A->audio_out_handle, p, sizeof(WAVEHDR));
+	      p->dwBufferLength = 0;
+	      p->dwUser = DWU_FILLING;
+	    }
+
+	    /* Should never be full at this point. */
+
+	    assert (p->dwBufferLength >= 0);
+	    assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
+
+	    p->lpData[p->dwBufferLength++] = c;
+
+	    if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
+	      return (audio_flush(a));
+	    }
+	    break;
 
-	if (p->dwUser == DWU_DONE) {
-	  waveOutUnprepareHeader (A->audio_out_handle, p, sizeof(WAVEHDR));
-	  p->dwBufferLength = 0;
-	  p->dwUser = DWU_FILLING;
-	}
 
-	/* Should never be full at this point. */
+	  case AUDIO_OUT_TYPE_SDR_UDP:
+	  case AUDIO_OUT_TYPE_STDOUT:
 
-	assert (p->dwBufferLength >= 0);
-	assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
+	    A->stream_out_data[A->stream_out_next++] = c;
 
-	p->lpData[p->dwBufferLength++] = c;
+	    assert(A->stream_out_next > 0);
+	    assert(A->stream_out_next <= SDR_UDP_BUF_MAXLEN);
 
-	if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
-	  return (audio_flush(a));
+	    if (A->stream_out_next == SDR_UDP_BUF_MAXLEN) {
+	      return (audio_flush(a));
+	    }
+	    break;
 	}
 
 	return (0);
@@ -1013,29 +1174,77 @@ int audio_flush (int a)
 	WAVEHDR *p;
 	MMRESULT e;
 	struct adev_s *A;
+	int res;
+	char *ptr;
+	unsigned int len;
+
 
 	A = &(adev[a]); 
-	
-	p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
 
-	if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) {
+	switch (A->g_audio_out_type) {
+	  case AUDIO_OUT_TYPE_SOUNDCARD:
+	    p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
 
-	  p->dwUser = DWU_PLAYING;
+	    if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) {
 
-	  waveOutPrepareHeader(A->audio_out_handle, p, sizeof(WAVEHDR));
+	      p->dwUser = DWU_PLAYING;
 
-	  e = waveOutWrite(A->audio_out_handle, p, sizeof(WAVEHDR));
-	  if (e != MMSYSERR_NOERROR) {
-	    text_color_set (DW_COLOR_ERROR);
-	    dw_printf ("audio out write error %d\n", e);
+	      waveOutPrepareHeader(A->audio_out_handle, p, sizeof(WAVEHDR));
 
-	    /* I don't expect this to ever happen but if it */
-	    /* does, make the buffer available for filling. */
+	      e = waveOutWrite(A->audio_out_handle, p, sizeof(WAVEHDR));
+	      if (e != MMSYSERR_NOERROR) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("audio out write error %d\n", e);
+
+	        /* I don't expect this to ever happen but if it */
+	        /* does, make the buffer available for filling. */
+
+	        p->dwUser = DWU_DONE;
+	        return (-1);
+	      }
+	      A->out_current = (A->out_current + 1) % NUM_OUT_BUF;
+	    }
+	    break;
+
+	  case AUDIO_OUT_TYPE_STDOUT:
+
+	    ptr = A->stream_out_data;
+	    len = A->stream_out_next;
+
+	    while (len > 0) {
+	      res = write(STDOUT_FILENO, ptr, len);
+	      if (res < 0) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("stdout audio write error %d\n", res);
+	        return (-1);
+	      }
+	      ptr += res;
+	      len -= res;
+	    }
+
+	    A->stream_out_next = 0;
+	    break;
+
+	  case AUDIO_OUT_TYPE_SDR_UDP:
+
+	    ptr = A->stream_out_data;
+	    len = A->stream_out_next;
+
+	    while (len > 0) {
+	      res = sendto(A->udp_out_sock, ptr, len, 0, (struct sockaddr *)&A->udp_dest_addr, sizeof(struct sockaddr_storage));
+	      if (res < 0) {
+	        text_color_set (DW_COLOR_ERROR);
+	        dw_printf ("Error %d writing to UDP socket.\n", res);
+	        return (-1);
+	      }
+
+	      ptr += res;
+	      len -= res;
+	    }
+
+	    A->stream_out_next = 0;
+	    break;
 
-	    p->dwUser = DWU_DONE;
-	    return (-1);
-	  }
-	  A->out_current = (A->out_current + 1) % NUM_OUT_BUF;
 	}
 	return (0);
 
diff --git a/src/ax25_pad2.c b/src/ax25_pad2.c
index 347df4b1..8b57f5bc 100644
--- a/src/ax25_pad2.c
+++ b/src/ax25_pad2.c
@@ -792,6 +792,8 @@ int main ()
 	strcpy (addrs[1], "WB2OSZ-15");
 	num_addr = 2;
 
+	text_color_init (1, 0);
+
 /* U frame */
 
 	for (ftype = frame_type_U_SABME; ftype <= frame_type_U_TEST; ftype++) {
diff --git a/src/cm108.c b/src/cm108.c
index ff3ff792..7ea1626c 100644
--- a/src/cm108.c
+++ b/src/cm108.c
@@ -113,7 +113,7 @@
 
 int main (void)
 {
-	text_color_init (0);    // Turn off text color.
+	text_color_init (0, 0);    // Turn off text color.
 #if defined(__OpenBSD__) || defined(__FreeBSD__)
 	dw_printf ("CM108 PTT support is not available for this operating system.\n");
 #else
@@ -340,7 +340,7 @@ int main (int argc, char **argv)
 	int num_things;
 	int i;
 
-	text_color_init (0);    // Turn off text color.
+	text_color_init (0, 0);    // Turn off text color.
 	text_color_set(DW_COLOR_INFO);
 
 	if (argc >=2) {
diff --git a/src/decode_aprs.c b/src/decode_aprs.c
index d96274bc..5e328960 100644
--- a/src/decode_aprs.c
+++ b/src/decode_aprs.c
@@ -5330,7 +5330,7 @@ int main (int argc, char *argv[])
 	}
 
 	// If you don't like the text colors, use 0 instead of 1 here.
-	text_color_init(1);
+	text_color_init(1, 0);
 	text_color_set(DW_COLOR_INFO);
 
 	while (fgets(stuff, sizeof(stuff), stdin) != NULL) 
diff --git a/src/digipeater.c b/src/digipeater.c
index 4daf1890..e3849820 100644
--- a/src/digipeater.c
+++ b/src/digipeater.c
@@ -765,6 +765,7 @@ int main (int argc, char *argv[])
 	strlcpy(mycall, "WB2OSZ-9", sizeof(mycall));
 
 	dedupe_init (4);
+	text_color_init (1, 0);
 
 /* 
  * Compile the patterns. 
diff --git a/src/direwolf.c b/src/direwolf.c
index 4f3c3030..d397db31 100644
--- a/src/direwolf.c
+++ b/src/direwolf.c
@@ -242,6 +242,8 @@ int main (int argc, char *argv[])
 	char x_opt_mode = ' ';		/* "-x N" option for transmitting calibration tones. */
 	int x_opt_chan = 0;		/* Split into 2 parts.  Mode e.g.  m, a, and optional channel. */
 
+	int O_opt = 0;			/* Redirect text io to stderr for use with stdout audio */
+
 	strlcpy(l_opt_logdir, "", sizeof(l_opt_logdir));
 	strlcpy(L_opt_logfile, "", sizeof(L_opt_logfile));
 	strlcpy(P_opt, "", sizeof(P_opt));
@@ -270,23 +272,28 @@ int main (int argc, char *argv[])
 #endif
 
 /*
- * Pre-scan the command line options for the text color option.
- * We need to set this before any text output.
+ * Pre-scan the command line options for the text color and stdout redirect options.
+ * We need to set these before any text output.
  * Default will be no colors if stdout is not a terminal (i.e. piped into
  * something else such as "tee") but command line can override this.
  */
+	for (j=1; j<argc; j++) {
+	  if (strcmp(argv[j], "-O") == 0) {
+	    O_opt = 1;
+	  }
+	}
 
 #if __WIN32__
-	t_opt = _isatty(_fileno(stdout)) > 0;
+	t_opt = _isatty(_fileno(O_opt ? stderr : stdout)) > 0;
 #else
-	t_opt = isatty(fileno(stdout));
+	t_opt = isatty(fileno(O_opt ? stderr : stdout));
 #endif
 				/* 1 = normal, 0 = no text colors. */
 				/* 2, 3, ... alternate escape sequences for different terminals. */
 
 // FIXME: consider case of no space between t and number.
 
-	for (j=1; j<argc-1; j++) {
+	for (j=1; j<argc; j++) {
 	  if (strcmp(argv[j], "-t") == 0) {
 	    t_opt = atoi (argv[j+1]);
 	    //dw_printf ("DEBUG: text color option = %d.\n", t_opt);
@@ -299,7 +306,7 @@ int main (int argc, char *argv[])
 	// Might want to print OS version here.   For Windows, see:
 	// https://msdn.microsoft.com/en-us/library/ms724451(v=VS.85).aspx
 
-	text_color_init(t_opt);
+	text_color_init(t_opt, O_opt);
 	text_color_set(DW_COLOR_INFO);
 	dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
 	//dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
@@ -421,7 +428,7 @@ int main (int argc, char *argv[])
 
 	  /* ':' following option character means arg is required. */
 
-          c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:AI:i:",
+          c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:AI:i:O",
                         long_options, &option_index);
           if (c == -1)
             break;
@@ -738,6 +745,10 @@ int main (int argc, char *argv[])
 	    A_opt_ais_to_obj = 1;
 	    break;
 
+	  case 'O':				/* Was handled earlier. -O Redirects output to stderr. */
+	    break;
+
+
           default:
 
             /* Should not be here. */
diff --git a/src/dtmf.c b/src/dtmf.c
index 953b0f70..f8a49da3 100644
--- a/src/dtmf.c
+++ b/src/dtmf.c
@@ -532,6 +532,7 @@ int main ()
 	my_audio_config.chan_medium[c] = MEDIUM_RADIO;
 	my_audio_config.achan[c].dtmf_decode = DTMF_DECODE_ON;
 
+	text_color_init (1, 0);
 	dtmf_init(&my_audio_config, 50);	
 
 	text_color_set(DW_COLOR_INFO);
diff --git a/src/encode_aprs.c b/src/encode_aprs.c
index 20992bf7..bf2636ad 100644
--- a/src/encode_aprs.c
+++ b/src/encode_aprs.c
@@ -853,6 +853,7 @@ int main (int argc, char *argv[])
 	char result[100];
 	int errors = 0;
 
+	text_color_init (1, 0);
 
 /***********  Position  ***********/
 
diff --git a/src/fx25_rec.c b/src/fx25_rec.c
index 9cb5c4d9..77a064dd 100644
--- a/src/fx25_rec.c
+++ b/src/fx25_rec.c
@@ -73,6 +73,7 @@ static int fx25_test_count = 0;
 
 int main ()
 {
+	text_color_init(1, 0);
 	fx25_init(3);
 
 	for (int i = CTAG_MIN; i <= CTAG_MAX; i++) {
@@ -480,4 +481,4 @@ static int my_unstuff (int chan, int subchan, int slice, unsigned char * restric
 
 }  // my_unstuff
 
-// end fx25_rec.c
\ No newline at end of file
+// end fx25_rec.c
diff --git a/src/fx25_send.c b/src/fx25_send.c
index 7435be9f..0ad5a3e6 100644
--- a/src/fx25_send.c
+++ b/src/fx25_send.c
@@ -55,6 +55,7 @@ static unsigned char preload[] = {
 
 int main ()
 {
+	text_color_init(1, 0);
 	text_color_set(DW_COLOR_ERROR);
 	dw_printf("fxsend - FX.25 unit test.\n");
 	dw_printf("This generates 11 files named fx01.dat, fx02.dat, ..., fx0b.dat\n");
@@ -333,4 +334,4 @@ static int stuff_it (unsigned char *in, int ilen, unsigned char *out, int osize)
 
 } // end stuff_it
 
-// end fx25_send.c
\ No newline at end of file
+// end fx25_send.c
diff --git a/src/gen_packets.c b/src/gen_packets.c
index 57b2741c..2315e1ea 100644
--- a/src/gen_packets.c
+++ b/src/gen_packets.c
@@ -265,6 +265,8 @@ int main(int argc, char **argv)
 
 	strlcpy (output_file, "", sizeof(output_file));
 
+	text_color_init (1, 0);
+
 /*
  * Parse the command line options.
  */
diff --git a/src/il2p_test.c b/src/il2p_test.c
index 17995259..ab965c98 100644
--- a/src/il2p_test.c
+++ b/src/il2p_test.c
@@ -53,7 +53,7 @@ static void decode_bitstream(void);
 int main ()
 {
 	int enable_color = 1;
-	text_color_init (enable_color);
+	text_color_init (enable_color, 0);
 
 	int enable_debug_out = 0;
 	il2p_init(enable_debug_out);
@@ -974,4 +974,4 @@ alevel_t demod_get_audio_level (int chan, int subchan)
 	return (alevel);
 }
 
-// end il2p_test.c
\ No newline at end of file
+// end il2p_test.c
diff --git a/src/kissutil.c b/src/kissutil.c
index fcd86088..46bcadf7 100644
--- a/src/kissutil.c
+++ b/src/kissutil.c
@@ -179,7 +179,7 @@ static void trim (char *stuff)
 
 int main (int argc, char *argv[])
 {
-	text_color_init (0);	// Turn off text color.
+	text_color_init (0, 0);	// Turn off text color.
 				// It could interfere with trying to pipe stdout to some other application.
 
 #if __WIN32__
diff --git a/src/latlong.c b/src/latlong.c
index d5f23365..731452dd 100644
--- a/src/latlong.c
+++ b/src/latlong.c
@@ -899,6 +899,8 @@ int main (int argc, char *argv[])
 	double dlat, dlon;
 	double d, b;
 
+	text_color_init (1, 0);
+
 /* Latitude to APRS format. */
 
 	latitude_to_str (45.25, 0, result);
@@ -1086,4 +1088,4 @@ int main (int argc, char *argv[])
 #endif
 
 
-/* end latlong.c */
\ No newline at end of file
+/* end latlong.c */
diff --git a/src/pfilter.c b/src/pfilter.c
index 35767a67..0811c009 100644
--- a/src/pfilter.c
+++ b/src/pfilter.c
@@ -1527,6 +1527,7 @@ static void pftest (int test_num, char *filter, char *packet, int expected);
 int main ()
 {
 
+	text_color_init (1, 0);
 	dw_printf ("Quick test for packet filtering.\n");
 	dw_printf ("Some error messages are normal.  Look at the final success/fail message.\n");
 
diff --git a/src/telemetry.c b/src/telemetry.c
index 2a6c690c..ce066236 100644
--- a/src/telemetry.c
+++ b/src/telemetry.c
@@ -1069,6 +1069,7 @@ int main ( )
 	strlcpy (comment, "", sizeof(comment));
 
 
+	text_color_init(1, 0);
 	text_color_set(DW_COLOR_INFO);
 	dw_printf ("Unit test for telemetry decoding functions...\n");	
 
diff --git a/src/textcolor.c b/src/textcolor.c
index dea90f09..453d8287 100644
--- a/src/textcolor.c
+++ b/src/textcolor.c
@@ -171,14 +171,19 @@ static const char clear_eos[]	= "\e[0J";
  */
 
 static int g_enable_color = 1;
+static FILE *g_dw_printf_dest = 0;
 
-
-void text_color_init (int enable_color)
+void text_color_init (int enable_color, int redirect_output)
 {
-
+	if (redirect_output != 0) {
+	  g_dw_printf_dest = stderr;
+	} else {
+	  g_dw_printf_dest = stdout;
+	}
 
 #if __WIN32__
 
+	g_enable_color = enable_color;
 
 	if (g_enable_color != 0) {
 
@@ -189,7 +194,12 @@ void text_color_init (int enable_color)
 	  COORD coord;
 	  DWORD nwritten;
 
-	  h = GetStdHandle(STD_OUTPUT_HANDLE);
+	  if (redirect_output != 0) {
+	    h = GetStdHandle(STD_ERROR_HANDLE);
+	  } else {
+	    h = GetStdHandle(STD_OUTPUT_HANDLE);
+	  }
+
 	  if (h != NULL && h != INVALID_HANDLE_VALUE) {
 
 	    GetConsoleScreenBufferInfo (h, &csbi);
@@ -208,18 +218,18 @@ void text_color_init (int enable_color)
 	if (enable_color < 0 || enable_color > MAX_T) {
 	  int t;
 	  for (t = 0; t <= MAX_T; t++) {
-	    text_color_init (t);
-	    printf ("-t %d", t);
-	    if (t) printf ("   [white background]   ");
-	    printf ("\n");
-	    printf ("%sBlack ", t_black[t]);
-	    printf ("%sRed ", t_red[t]);
-	    printf ("%sGreen ", t_green[t]);
-	    printf ("%sDark-Green ", t_dark_green[t]);
-	    printf ("%sYellow ", t_yellow[t]);
-	    printf ("%sBlue ", t_blue[t]);
-	    printf ("%sMagenta ", t_magenta[t]);
-	    printf ("%sCyan   \n", t_cyan[t]);
+	    text_color_init (t, redirect_output);
+	    fprintf (g_dw_printf_dest,"-t %d", t);
+	    if (t) fprintf (g_dw_printf_dest, "   [white background]   ");
+	    fprintf (g_dw_printf_dest,"\n");
+	    fprintf (g_dw_printf_dest,"%sBlack ", t_black[t]);
+	    fprintf (g_dw_printf_dest,"%sRed ", t_red[t]);
+	    fprintf (g_dw_printf_dest,"%sGreen ", t_green[t]);
+	    fprintf (g_dw_printf_dest,"%sDark-Green ", t_dark_green[t]);
+	    fprintf (g_dw_printf_dest,"%sYellow ", t_yellow[t]);
+	    fprintf (g_dw_printf_dest,"%sBlue ", t_blue[t]);
+	    fprintf (g_dw_printf_dest, "%sMagenta ", t_magenta[t]);
+	    fprintf (g_dw_printf_dest, "%sCyan   \n", t_cyan[t]);
 	   }
 	   exit (EXIT_SUCCESS);
 	}
@@ -232,9 +242,9 @@ void text_color_init (int enable_color)
 	  if (t < 0) t = 0;
 	  if (t > MAX_T) t = MAX_T;
 
-	  printf ("%s", t_background_white[t]);
-	  printf ("%s", clear_eos);
-	  printf ("%s", t_black[t]);
+	  fprintf (g_dw_printf_dest, "%s", t_background_white[t]);
+	  fprintf (g_dw_printf_dest, "%s", clear_eos);
+	  fprintf (g_dw_printf_dest, "%s", t_black[t]);
 	}
 #endif
 }
@@ -285,7 +295,11 @@ void text_color_set ( enum dw_color_e c )
 	    break;
 	}
 
-	h = GetStdHandle(STD_OUTPUT_HANDLE);
+	if (dw_printf_redirected()) {
+	  h = GetStdHandle(STD_ERROR_HANDLE);
+	} else {
+	  h = GetStdHandle(STD_OUTPUT_HANDLE);
+	}
 
 	if (h != NULL && h != INVALID_HANDLE_VALUE) {
 	  SetConsoleTextAttribute (h, attr);
@@ -310,30 +324,30 @@ void text_color_set ( enum dw_color_e c )
 
 	  default:
 	  case DW_COLOR_INFO:
-	    printf ("%s", t_black[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_black[t]);
 	    break;
 
 	  case DW_COLOR_ERROR:
-	    printf ("%s", t_red[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_red[t]);
 	    break;
 
 	  case DW_COLOR_REC:
 	    // Bright green is very difficult to read against a while background.
 	    // Let's use dark green instead.   release 1.6.
 	    //printf ("%s", t_green[t]);
-	    printf ("%s", t_dark_green[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_dark_green[t]);
 	    break;
 
 	  case DW_COLOR_DECODED:
-	    printf ("%s", t_blue[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_blue[t]);
 	    break;
 
 	  case DW_COLOR_XMIT:
-	    printf ("%s", t_magenta[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_magenta[t]);
 	    break;
 
 	  case DW_COLOR_DEBUG:
-	    printf ("%s", t_dark_green[t]);
+	    fprintf (g_dw_printf_dest, "%s", t_dark_green[t]);
 	    break;
 	}
 }
@@ -377,17 +391,21 @@ int dw_printf (const char *fmt, ...)
 
 // TODO: other possible destinations...
 
-	fputs (buffer, stdout);
+	fputs (buffer, g_dw_printf_dest);
+	fflush (g_dw_printf_dest);
 	return (len);
 }
 
-
+int dw_printf_redirected ()
+{
+	return g_dw_printf_dest != stdout;
+}
 
 #if TESTC
 main () 
 {
 	printf ("Initial condition\n");
-	text_color_init (1);
+	text_color_init (1, 0);
 	printf ("After text_color_init\n");
 	text_color_set(DW_COLOR_INFO); 		printf ("Info\n");
 	text_color_set(DW_COLOR_ERROR); 	printf ("Error\n");
diff --git a/src/textcolor.h b/src/textcolor.h
index 4e38c83e..123430e5 100644
--- a/src/textcolor.h
+++ b/src/textcolor.h
@@ -22,7 +22,7 @@ enum dw_color_e { 	DW_COLOR_INFO,		/* black */
 typedef enum dw_color_e dw_color_t;
 
 			
-void text_color_init (int enable_color);
+void text_color_init (int enable_color, int redirect_output);
 void text_color_set (dw_color_t c);
 void text_color_term (void);
 
@@ -55,4 +55,6 @@ int dw_printf (const char *fmt, ...)
 				__attribute__((format(printf,1,2)));		/* gnu C lib. */
 #endif
 
+int dw_printf_redirected ();
+
 #endif
diff --git a/src/tt_text.c b/src/tt_text.c
index 112adfe5..e13ac96f 100644
--- a/src/tt_text.c
+++ b/src/tt_text.c
@@ -1782,6 +1782,7 @@ static void test_tt2text (char *buttons, char *expect_mp, char *expect_2k, char
 int main (int argc, char *argv[])
 {
 
+	text_color_init (1, 0);
 	text_color_set (DW_COLOR_INFO);
 	dw_printf ("Test conversions between normal text and DTMF representation.\n");
 	dw_printf ("Some error messages are normal.  Just look for number of errors at end.\n");
diff --git a/src/xid.c b/src/xid.c
index 14e67e8d..f1f7981d 100644
--- a/src/xid.c
+++ b/src/xid.c
@@ -653,6 +653,7 @@ int main (int argc, char *argv[]) {
 	unsigned char info[40];	// Currently max of 27 but things can change.
 	char desc[150];		// I've seen 109.
 
+	text_color_init (1, 0);
 
 /* parse example. */