mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			Merge 6c62d30416 into ab834f338b
				
					
				
			This commit is contained in:
		
						commit
						e5c01c82f7
					
				|  | @ -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: ### | ||||
|  |  | |||
|  | @ -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% | ||||
|  |  | |||
|  | @ -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 "  | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
| /* 
 | ||||
|  |  | |||
							
								
								
									
										478
									
								
								src/audio.c
								
								
								
								
							
							
						
						
									
										478
									
								
								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 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 */ | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
							
								
								
									
										369
									
								
								src/audio_win.c
								
								
								
								
							
							
						
						
									
										369
									
								
								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; | ||||
| 	} | ||||
| 	    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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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++) { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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)  | ||||
|  |  | |||
|  | @ -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.  | ||||
|  |  | |||
|  | @ -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. */ | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -853,6 +853,7 @@ int main (int argc, char *argv[]) | |||
| 	char result[100]; | ||||
| 	int errors = 0; | ||||
| 
 | ||||
| 	text_color_init (1, 0); | ||||
| 
 | ||||
| /***********  Position  ***********/ | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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. | ||||
|  */ | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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__ | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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"); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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");	 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue