This commit is contained in:
Matthew McDougal, KA0S 2023-09-25 22:03:53 +00:00 committed by GitHub
commit e5c01c82f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 723 additions and 300 deletions

View File

@ -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: ###

View File

@ -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%
%C%

View File

@ -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 "

View File

@ -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;

View File

@ -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);
/*

View File

@ -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 USE_ALSA
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");
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);
}
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;
}
adev[a].outbuf_size_in_bytes = set_alsa_params (a, adev[a].audio_out_handle, pa, audio_out_name, "output");
switch (adev[a].g_audio_out_type) {
case AUDIO_OUT_TYPE_STDOUT:
adev[a].outbuf_size_in_bytes = 1024;
break;
if (adev[a].inbuf_size_in_bytes <= 0 || adev[a].outbuf_size_in_bytes <= 0) {
return (-1);
}
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);
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");
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)
{
#if USE_ALSA
int k;
unsigned char *psound;
int retries = 10;
snd_pcm_status_t *status;
int res;
unsigned char *ptr;
int len;
assert (adev[a].audio_out_handle != NULL);
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;
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) {
if (k == -EPIPE) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio output data underrun.\n");
/* 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));
/* 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. */
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 output data underrun.\n");
dw_printf ("Audio write error retry count exceeded.\n");
/* 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));
/* 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. */
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");
adev[a].outbuf_len = 0;
return (-1);
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");
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 (-1);
}
if (k < len) {
/* presumably full but didn't block. */
usleep (10000);
}
ptr += k;
len -= k;
}
adev[a].outbuf_len = 0;
return (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 */

View File

@ -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 {

View File

@ -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;
}
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. */
/* Should never be full at this point. */
assert (p->dwBufferLength >= 0);
assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
assert (p->dwBufferLength >= 0);
assert (p->dwBufferLength < (DWORD)(A->outbuf_size));
p->lpData[p->dwBufferLength++] = c;
p->lpData[p->dwBufferLength++] = c;
if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
return (audio_flush(a));
if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
return (audio_flush(a));
}
break;
case AUDIO_OUT_TYPE_SDR_UDP:
case AUDIO_OUT_TYPE_STDOUT:
A->stream_out_data[A->stream_out_next++] = c;
assert(A->stream_out_next > 0);
assert(A->stream_out_next <= SDR_UDP_BUF_MAXLEN);
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);

View File

@ -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++) {

View File

@ -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) {

View File

@ -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)

View File

@ -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.

View File

@ -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. */

View File

@ -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);

View File

@ -853,6 +853,7 @@ int main (int argc, char *argv[])
char result[100];
int errors = 0;
text_color_init (1, 0);
/*********** Position ***********/

View File

@ -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
// end fx25_rec.c

View File

@ -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
// end fx25_send.c

View File

@ -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.
*/

View File

@ -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
// end il2p_test.c

View File

@ -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__

View File

@ -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 */
/* end latlong.c */

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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");

View File

@ -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. */