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) > 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: ### ### Bugs Fixed: ###

View File

@ -132,10 +132,15 @@
%W%# "stdin" is not an audio device. Don't use this unless you %W%# "stdin" is not an audio device. Don't use this unless you
%W%# understand what this means. Read the User Guide. %W%# understand what this means. Read the User Guide.
%W%# You can also specify "UDP:" and an optional port for input. %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%
%W%# ADEVICE stdin 0 %W%# ADEVICE stdin 0
%W%# ADEVICE UDP:7355 0 %W%# ADEVICE UDP:7355 0
%W%# ADEVICE UDP:7355 UDP:localhost:7356
%W%# ADEVICE stdin stdout
%W% %W%
%W%# The position in the list can change when devices (e.g. USB) are added and removed. %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. %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%# "stdin" is not an audio device. Don't use this unless you
%L%# understand what this means. Read the User Guide. %L%# understand what this means. Read the User Guide.
%L%# You can also specify "UDP:" and an optional port for input. %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%
%L%# ADEVICE stdin plughw:1,0 %L%# ADEVICE stdin plughw:1,0
%L%# ADEVICE UDP:7355 default %L%# ADEVICE UDP:7355 default
%L%# ADEVICE UDP:7355 UDP:localhost:7356
%L%# ADEVICE stdin stdout
%L% %L%
%R% ---------- Mac ---------- %R% ---------- Mac ----------
%R% %R%
@ -183,9 +193,14 @@
%M%# "stdin" is not an audio device. Don't use this unless you %M%# "stdin" is not an audio device. Don't use this unless you
%M%# understand what this means. Read the User Guide. %M%# understand what this means. Read the User Guide.
%M%# You can also specify "UDP:" and an optional port for input. %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%
%M%# ADEVICE UDP:7355 default %M%# ADEVICE UDP:7355 default
%M%# ADEVICE UDP:7355 UDP:localhost:7356
%M%# ADEVICE stdin stdout
%M%# %M%#
%C% %C%
%C%# %C%#

View File

@ -155,6 +155,9 @@ x = Silence FX.25 information.
.BI "-t " "n" .BI "-t " "n"
Text colors. 0=disabled. 1=default. 2,3,4,... alternatives. Use 9 to test compatibility with your terminal. 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 .TP
.B "-p " .B "-p "

View File

@ -2078,6 +2078,7 @@ static void check_result (void)
int main (int argc, char *argv[]) int main (int argc, char *argv[])
{ {
text_color_init (1, 0);
aprs_tt_init (NULL, 0); aprs_tt_init (NULL, 0);
error_count = 0; error_count = 0;

View File

@ -217,7 +217,7 @@ int main (int argc, char *argv[])
} }
#endif #endif
text_color_init(1); text_color_init(1, 0);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
/* /*

View File

@ -75,6 +75,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h>
#include <errno.h> #include <errno.h>
@ -129,8 +130,11 @@ static struct adev_s {
int outbuf_len; int outbuf_len;
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;
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]; } adev[MAX_ADEVS];
@ -222,8 +226,8 @@ int audio_open (struct audio_s *pa)
#endif #endif
int chan; int chan;
int a; int a;
char audio_in_name[30]; char audio_in_name[80];
char audio_out_name[30]; char audio_out_name[80];
save_audio_config_p = pa; save_audio_config_p = pa;
@ -238,7 +242,7 @@ int audio_open (struct audio_s *pa)
#else #else
adev[a].oss_audio_device_fd = -1; adev[a].oss_audio_device_fd = -1;
#endif #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; //int data_size = 0;
//Create UDP Socket //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); text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't create socket, errno %d\n", errno); dw_printf ("Couldn't create socket, errno %d\n", errno);
return -1; return -1;
@ -424,7 +428,7 @@ int audio_open (struct audio_s *pa)
si_me.sin_addr.s_addr = htonl(INADDR_ANY); si_me.sin_addr.s_addr = htonl(INADDR_ANY);
//Bind to the socket //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); text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't bind socket, errno %d\n", errno); dw_printf ("Couldn't bind socket, errno %d\n", errno);
return -1; return -1;
@ -453,9 +457,35 @@ 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 #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);
@ -498,7 +528,63 @@ int audio_open (struct audio_s *pa)
return (-1); return (-1);
} }
#endif #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. * Finally allocate buffer for each direction.
*/ */
@ -1205,8 +1291,8 @@ int audio_get (int a)
while (adev[a].inbuf_next >= adev[a].inbuf_len) { while (adev[a].inbuf_next >= adev[a].inbuf_len) {
int res; int res;
assert (adev[a].udp_sock > 0); assert (adev[a].udp_in_sock > 0);
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0); res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
if (res < 0) { if (res < 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't read from udp socket, res=%d", res); dw_printf ("Can't read from udp socket, res=%d", res);
@ -1333,6 +1419,30 @@ int audio_put (int a, int c)
int audio_flush (int a) 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 #if USE_ALSA
int k; int k;
unsigned char *psound; unsigned char *psound;
@ -1505,6 +1615,33 @@ int audio_flush (int a)
return (0); return (0);
#endif #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 */ } /* end audio_flush */
@ -1546,9 +1683,12 @@ int audio_flush (int a)
void audio_wait (int a) void audio_wait (int a)
{ {
audio_flush (a); audio_flush (a);
if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
return;
}
#if USE_ALSA #if USE_ALSA
/* For playback, this should wait for all pending frames */ /* 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_SDR_UDP,
AUDIO_IN_TYPE_STDIN }; 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. */ /* For option to try fixing frames with bad CRC. */
typedef enum retry_e { typedef enum retry_e {

View File

@ -134,7 +134,7 @@ static int calcbufsize(int rate, int chans, int bits)
static struct adev_s { 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. * UDP socket for receiving audio stream.
* Buffer, length, and pointer for UDP or stdin. * Buffer, length, and pointer for UDP or stdin.
@ -146,6 +146,14 @@ static struct adev_s {
int stream_len; int stream_len;
int stream_next; 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. */ /* For sound output. */
/* out_wavehdr.dwUser is used to keep track of output buffer state. */ /* 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_sock = INVALID_SOCKET;
A->udp_out_sock = INVALID_SOCKET;
in_dev_no[a] = WAVE_MAPPER; /* = ((UINT)-1) in mmsystem.h */ in_dev_no[a] = WAVE_MAPPER; /* = ((UINT)-1) in mmsystem.h */
out_dev_no[a] = WAVE_MAPPER; out_dev_no[a] = WAVE_MAPPER;
@ -343,9 +352,30 @@ int audio_open (struct audio_s *pa)
/* /*
* Select output device. * Select output device.
* Only soundcard at this point. * Soundcard, UDP, and stdout supported.
* Purhaps we'd like to add UDP for an SDR transmitter.
*/ */
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 (strlen(pa->adev[a].adevice_out) == 1 && isdigit(pa->adev[a].adevice_out[0])) { 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); out_dev_no[a] = atoi(pa->adev[a].adevice_out);
} }
@ -367,6 +397,7 @@ int audio_open (struct audio_s *pa)
dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adev[a].adevice_out); dw_printf ("\"%s\" doesn't match any of the output devices.\n", pa->adev[a].adevice_out);
} }
} }
}
} /* if defined */ } /* if defined */
} /* for each device */ } /* for each device */
@ -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. * Open for each audio device input/output pair.
@ -523,17 +584,20 @@ int audio_open (struct audio_s *pa)
/* /*
* Open the audio output device. * Open the audio output device.
* Soundcard is only possibility at this time. * Soundcard and stdout are only possibility at this time.
*/ */
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); err = waveOutOpen (&(A->audio_out_handle), out_dev_no[a], &wf, (DWORD_PTR)out_callback, a, CALLBACK_FUNCTION);
if (err != MMSYSERR_NOERROR) { if (err != MMSYSERR_NOERROR) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Could not open audio device for output.\n"); dw_printf ("Could not open audio device for output.\n");
return (-1); return (-1);
} }
break;
/* /*
* Set up the output buffers. * Set up the output buffers.
* We use dwUser to indicate it is available for filling. * We use dwUser to indicate it is available for filling.
@ -548,6 +612,83 @@ int audio_open (struct audio_s *pa)
} }
A->out_current = 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. * Open audio input device.
@ -943,6 +1084,10 @@ int audio_put (int a, int c)
struct adev_s *A; struct adev_s *A;
A = &(adev[a]); A = &(adev[a]);
switch (A->g_audio_out_type) {
case AUDIO_OUT_TYPE_SOUNDCARD:
/* /*
* Wait if no buffers are available. * Wait if no buffers are available.
* Don't use p yet because compiler might might consider dwFlags a loop invariant. * Don't use p yet because compiler might might consider dwFlags a loop invariant.
@ -986,6 +1131,22 @@ int audio_put (int a, int c)
if (p->dwBufferLength == (DWORD)(A->outbuf_size)) { if (p->dwBufferLength == (DWORD)(A->outbuf_size)) {
return (audio_flush(a)); 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); return (0);
@ -1013,9 +1174,15 @@ int audio_flush (int a)
WAVEHDR *p; WAVEHDR *p;
MMRESULT e; MMRESULT e;
struct adev_s *A; struct adev_s *A;
int res;
char *ptr;
unsigned int len;
A = &(adev[a]); A = &(adev[a]);
switch (A->g_audio_out_type) {
case AUDIO_OUT_TYPE_SOUNDCARD:
p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current])); p = (LPWAVEHDR)(&(A->out_wavehdr[A->out_current]));
if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) { if (p->dwUser == DWU_FILLING && p->dwBufferLength > 0) {
@ -1037,6 +1204,48 @@ int audio_flush (int a)
} }
A->out_current = (A->out_current + 1) % NUM_OUT_BUF; 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;
}
return (0); return (0);
} /* end audio_flush */ } /* end audio_flush */

View File

@ -792,6 +792,8 @@ int main ()
strcpy (addrs[1], "WB2OSZ-15"); strcpy (addrs[1], "WB2OSZ-15");
num_addr = 2; num_addr = 2;
text_color_init (1, 0);
/* U frame */ /* U frame */
for (ftype = frame_type_U_SABME; ftype <= frame_type_U_TEST; ftype++) { for (ftype = frame_type_U_SABME; ftype <= frame_type_U_TEST; ftype++) {

View File

@ -113,7 +113,7 @@
int main (void) 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__) #if defined(__OpenBSD__) || defined(__FreeBSD__)
dw_printf ("CM108 PTT support is not available for this operating system.\n"); dw_printf ("CM108 PTT support is not available for this operating system.\n");
#else #else
@ -340,7 +340,7 @@ int main (int argc, char **argv)
int num_things; int num_things;
int i; 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); text_color_set(DW_COLOR_INFO);
if (argc >=2) { 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. // 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); text_color_set(DW_COLOR_INFO);
while (fgets(stuff, sizeof(stuff), stdin) != NULL) 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)); strlcpy(mycall, "WB2OSZ-9", sizeof(mycall));
dedupe_init (4); dedupe_init (4);
text_color_init (1, 0);
/* /*
* Compile the patterns. * 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. */ 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 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_logdir, "", sizeof(l_opt_logdir));
strlcpy(L_opt_logfile, "", sizeof(L_opt_logfile)); strlcpy(L_opt_logfile, "", sizeof(L_opt_logfile));
strlcpy(P_opt, "", sizeof(P_opt)); strlcpy(P_opt, "", sizeof(P_opt));
@ -270,23 +272,28 @@ int main (int argc, char *argv[])
#endif #endif
/* /*
* Pre-scan the command line options for the text color option. * Pre-scan the command line options for the text color and stdout redirect options.
* We need to set this before any text output. * We need to set these before any text output.
* Default will be no colors if stdout is not a terminal (i.e. piped into * 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. * 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__ #if __WIN32__
t_opt = _isatty(_fileno(stdout)) > 0; t_opt = _isatty(_fileno(O_opt ? stderr : stdout)) > 0;
#else #else
t_opt = isatty(fileno(stdout)); t_opt = isatty(fileno(O_opt ? stderr : stdout));
#endif #endif
/* 1 = normal, 0 = no text colors. */ /* 1 = normal, 0 = no text colors. */
/* 2, 3, ... alternate escape sequences for different terminals. */ /* 2, 3, ... alternate escape sequences for different terminals. */
// FIXME: consider case of no space between t and number. // 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) { if (strcmp(argv[j], "-t") == 0) {
t_opt = atoi (argv[j+1]); t_opt = atoi (argv[j+1]);
//dw_printf ("DEBUG: text color option = %d.\n", t_opt); //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: // Might want to print OS version here. For Windows, see:
// https://msdn.microsoft.com/en-us/library/ms724451(v=VS.85).aspx // 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); 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 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__); //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. */ /* ':' 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); long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
@ -738,6 +745,10 @@ int main (int argc, char *argv[])
A_opt_ais_to_obj = 1; A_opt_ais_to_obj = 1;
break; break;
case 'O': /* Was handled earlier. -O Redirects output to stderr. */
break;
default: default:
/* Should not be here. */ /* Should not be here. */

View File

@ -532,6 +532,7 @@ int main ()
my_audio_config.chan_medium[c] = MEDIUM_RADIO; my_audio_config.chan_medium[c] = MEDIUM_RADIO;
my_audio_config.achan[c].dtmf_decode = DTMF_DECODE_ON; my_audio_config.achan[c].dtmf_decode = DTMF_DECODE_ON;
text_color_init (1, 0);
dtmf_init(&my_audio_config, 50); dtmf_init(&my_audio_config, 50);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);

View File

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

View File

@ -73,6 +73,7 @@ static int fx25_test_count = 0;
int main () int main ()
{ {
text_color_init(1, 0);
fx25_init(3); fx25_init(3);
for (int i = CTAG_MIN; i <= CTAG_MAX; i++) { for (int i = CTAG_MIN; i <= CTAG_MAX; i++) {

View File

@ -55,6 +55,7 @@ static unsigned char preload[] = {
int main () int main ()
{ {
text_color_init(1, 0);
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf("fxsend - FX.25 unit test.\n"); dw_printf("fxsend - FX.25 unit test.\n");
dw_printf("This generates 11 files named fx01.dat, fx02.dat, ..., fx0b.dat\n"); dw_printf("This generates 11 files named fx01.dat, fx02.dat, ..., fx0b.dat\n");

View File

@ -265,6 +265,8 @@ int main(int argc, char **argv)
strlcpy (output_file, "", sizeof(output_file)); strlcpy (output_file, "", sizeof(output_file));
text_color_init (1, 0);
/* /*
* Parse the command line options. * Parse the command line options.
*/ */

View File

@ -53,7 +53,7 @@ static void decode_bitstream(void);
int main () int main ()
{ {
int enable_color = 1; int enable_color = 1;
text_color_init (enable_color); text_color_init (enable_color, 0);
int enable_debug_out = 0; int enable_debug_out = 0;
il2p_init(enable_debug_out); il2p_init(enable_debug_out);

View File

@ -179,7 +179,7 @@ static void trim (char *stuff)
int main (int argc, char *argv[]) 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. // It could interfere with trying to pipe stdout to some other application.
#if __WIN32__ #if __WIN32__

View File

@ -899,6 +899,8 @@ int main (int argc, char *argv[])
double dlat, dlon; double dlat, dlon;
double d, b; double d, b;
text_color_init (1, 0);
/* Latitude to APRS format. */ /* Latitude to APRS format. */
latitude_to_str (45.25, 0, result); latitude_to_str (45.25, 0, result);

View File

@ -1527,6 +1527,7 @@ static void pftest (int test_num, char *filter, char *packet, int expected);
int main () int main ()
{ {
text_color_init (1, 0);
dw_printf ("Quick test for packet filtering.\n"); dw_printf ("Quick test for packet filtering.\n");
dw_printf ("Some error messages are normal. Look at the final success/fail message.\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)); strlcpy (comment, "", sizeof(comment));
text_color_init(1, 0);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Unit test for telemetry decoding functions...\n"); 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 int g_enable_color = 1;
static FILE *g_dw_printf_dest = 0;
void text_color_init (int enable_color, int redirect_output)
void text_color_init (int enable_color)
{ {
if (redirect_output != 0) {
g_dw_printf_dest = stderr;
} else {
g_dw_printf_dest = stdout;
}
#if __WIN32__ #if __WIN32__
g_enable_color = enable_color;
if (g_enable_color != 0) { if (g_enable_color != 0) {
@ -189,7 +194,12 @@ void text_color_init (int enable_color)
COORD coord; COORD coord;
DWORD nwritten; DWORD nwritten;
if (redirect_output != 0) {
h = GetStdHandle(STD_ERROR_HANDLE);
} else {
h = GetStdHandle(STD_OUTPUT_HANDLE); h = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (h != NULL && h != INVALID_HANDLE_VALUE) { if (h != NULL && h != INVALID_HANDLE_VALUE) {
GetConsoleScreenBufferInfo (h, &csbi); GetConsoleScreenBufferInfo (h, &csbi);
@ -208,18 +218,18 @@ void text_color_init (int enable_color)
if (enable_color < 0 || enable_color > MAX_T) { if (enable_color < 0 || enable_color > MAX_T) {
int t; int t;
for (t = 0; t <= MAX_T; t++) { for (t = 0; t <= MAX_T; t++) {
text_color_init (t); text_color_init (t, redirect_output);
printf ("-t %d", t); fprintf (g_dw_printf_dest,"-t %d", t);
if (t) printf (" [white background] "); if (t) fprintf (g_dw_printf_dest, " [white background] ");
printf ("\n"); fprintf (g_dw_printf_dest,"\n");
printf ("%sBlack ", t_black[t]); fprintf (g_dw_printf_dest,"%sBlack ", t_black[t]);
printf ("%sRed ", t_red[t]); fprintf (g_dw_printf_dest,"%sRed ", t_red[t]);
printf ("%sGreen ", t_green[t]); fprintf (g_dw_printf_dest,"%sGreen ", t_green[t]);
printf ("%sDark-Green ", t_dark_green[t]); fprintf (g_dw_printf_dest,"%sDark-Green ", t_dark_green[t]);
printf ("%sYellow ", t_yellow[t]); fprintf (g_dw_printf_dest,"%sYellow ", t_yellow[t]);
printf ("%sBlue ", t_blue[t]); fprintf (g_dw_printf_dest,"%sBlue ", t_blue[t]);
printf ("%sMagenta ", t_magenta[t]); fprintf (g_dw_printf_dest, "%sMagenta ", t_magenta[t]);
printf ("%sCyan \n", t_cyan[t]); fprintf (g_dw_printf_dest, "%sCyan \n", t_cyan[t]);
} }
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
} }
@ -232,9 +242,9 @@ void text_color_init (int enable_color)
if (t < 0) t = 0; if (t < 0) t = 0;
if (t > MAX_T) t = MAX_T; if (t > MAX_T) t = MAX_T;
printf ("%s", t_background_white[t]); fprintf (g_dw_printf_dest, "%s", t_background_white[t]);
printf ("%s", clear_eos); fprintf (g_dw_printf_dest, "%s", clear_eos);
printf ("%s", t_black[t]); fprintf (g_dw_printf_dest, "%s", t_black[t]);
} }
#endif #endif
} }
@ -285,7 +295,11 @@ void text_color_set ( enum dw_color_e c )
break; break;
} }
if (dw_printf_redirected()) {
h = GetStdHandle(STD_ERROR_HANDLE);
} else {
h = GetStdHandle(STD_OUTPUT_HANDLE); h = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (h != NULL && h != INVALID_HANDLE_VALUE) { if (h != NULL && h != INVALID_HANDLE_VALUE) {
SetConsoleTextAttribute (h, attr); SetConsoleTextAttribute (h, attr);
@ -310,30 +324,30 @@ void text_color_set ( enum dw_color_e c )
default: default:
case DW_COLOR_INFO: case DW_COLOR_INFO:
printf ("%s", t_black[t]); fprintf (g_dw_printf_dest, "%s", t_black[t]);
break; break;
case DW_COLOR_ERROR: case DW_COLOR_ERROR:
printf ("%s", t_red[t]); fprintf (g_dw_printf_dest, "%s", t_red[t]);
break; break;
case DW_COLOR_REC: case DW_COLOR_REC:
// Bright green is very difficult to read against a while background. // Bright green is very difficult to read against a while background.
// Let's use dark green instead. release 1.6. // Let's use dark green instead. release 1.6.
//printf ("%s", t_green[t]); //printf ("%s", t_green[t]);
printf ("%s", t_dark_green[t]); fprintf (g_dw_printf_dest, "%s", t_dark_green[t]);
break; break;
case DW_COLOR_DECODED: case DW_COLOR_DECODED:
printf ("%s", t_blue[t]); fprintf (g_dw_printf_dest, "%s", t_blue[t]);
break; break;
case DW_COLOR_XMIT: case DW_COLOR_XMIT:
printf ("%s", t_magenta[t]); fprintf (g_dw_printf_dest, "%s", t_magenta[t]);
break; break;
case DW_COLOR_DEBUG: case DW_COLOR_DEBUG:
printf ("%s", t_dark_green[t]); fprintf (g_dw_printf_dest, "%s", t_dark_green[t]);
break; break;
} }
} }
@ -377,17 +391,21 @@ int dw_printf (const char *fmt, ...)
// TODO: other possible destinations... // TODO: other possible destinations...
fputs (buffer, stdout); fputs (buffer, g_dw_printf_dest);
fflush (g_dw_printf_dest);
return (len); return (len);
} }
int dw_printf_redirected ()
{
return g_dw_printf_dest != stdout;
}
#if TESTC #if TESTC
main () main ()
{ {
printf ("Initial condition\n"); printf ("Initial condition\n");
text_color_init (1); text_color_init (1, 0);
printf ("After text_color_init\n"); printf ("After text_color_init\n");
text_color_set(DW_COLOR_INFO); printf ("Info\n"); text_color_set(DW_COLOR_INFO); printf ("Info\n");
text_color_set(DW_COLOR_ERROR); printf ("Error\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; 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_set (dw_color_t c);
void text_color_term (void); void text_color_term (void);
@ -55,4 +55,6 @@ int dw_printf (const char *fmt, ...)
__attribute__((format(printf,1,2))); /* gnu C lib. */ __attribute__((format(printf,1,2))); /* gnu C lib. */
#endif #endif
int dw_printf_redirected ();
#endif #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[]) int main (int argc, char *argv[])
{ {
text_color_init (1, 0);
text_color_set (DW_COLOR_INFO); text_color_set (DW_COLOR_INFO);
dw_printf ("Test conversions between normal text and DTMF representation.\n"); 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"); 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. unsigned char info[40]; // Currently max of 27 but things can change.
char desc[150]; // I've seen 109. char desc[150]; // I've seen 109.
text_color_init (1, 0);
/* parse example. */ /* parse example. */