From 0c86ba29fcd0d4443d9262e9b42fbcc8522c6720 Mon Sep 17 00:00:00 2001 From: lwvmobile Date: Mon, 21 Aug 2023 15:20:53 -0400 Subject: [PATCH] UDP Audio Output -- Initial Setup; --- include/dsd.h | 9 +++-- src/dsd_main.c | 55 +++++++++++++++++++++++++++-- src/dsd_ncurses.c | 36 ++++++++++++++----- src/dsd_rigctl.c | 90 ++++++++++++++++++++++++++++------------------- 4 files changed, 140 insertions(+), 50 deletions(-) diff --git a/include/dsd.h b/include/dsd.h index dcd01b8..3e95053 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -331,11 +331,10 @@ typedef struct int rigctlportno; char rigctlhostname[1024]; - //udp socket for GQRX, SDR++, etc + //UDP Socket Blaster Audio int udp_sockfd; int udp_portno; - char * udp_hostname; - SNDFILE *udp_file_in; + char udp_hostname[1024]; //tcp socket for SDR++, etc int tcp_sockfd; @@ -1224,6 +1223,10 @@ int csvChanImport(dsd_opts * opts, dsd_state * state); int csvKeyImportDec(dsd_opts * opts, dsd_state * state); int csvKeyImportHex(dsd_opts * opts, dsd_state * state); +//UDP Socket Connect and UDP Socket Blaster (audio output) +int udp_socket_connect(dsd_opts * opts, dsd_state * state); +int udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data); + #ifdef __cplusplus extern "C" { #endif diff --git a/src/dsd_main.c b/src/dsd_main.c index 6708662..b251337 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -675,10 +675,10 @@ initOpts (dsd_opts * opts) opts->rigctlportno = 4532; //TCP Port Number; GQRX - 7356; SDR++ - 4532 sprintf (opts->rigctlhostname, "%s", "localhost"); - //udp input options + //UDP Socket Blaster Audio opts->udp_sockfd = 0; - opts->udp_portno = 7355; //default favored by GQRX and SDR++ - opts->udp_hostname = "localhost"; + opts->udp_portno = 23456; //default port, same os OP25's sockaudio.py + sprintf (opts->udp_hostname, "%s", "0.0.0.0"); //tcp input options opts->tcp_sockfd = 0; @@ -1203,6 +1203,8 @@ usage () printf (" /dev/dsp for OSS audio (Depreciated: Will require padsp wrapper in Linux) \n"); #endif printf (" null for no audio output\n"); + printf (" udp for UDP socket blaster output (default host 0.0.0.0 (broadcast) default port 23456)\n"); + printf (" udp:127.0.0.1:23470 for UDP socket blaster output (Custom Address and Port\n"); printf (" -d Create mbe data files, use this directory (TDMA version is experimental)\n"); printf (" -r Read/Play saved mbe data from file(s)\n"); printf (" -g Audio Output Gain (Default: 0 = Auto; )\n"); @@ -1470,6 +1472,9 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state) ncursesClose(opts); } + if (opts->udp_sockfd) + close (opts->udp_sockfd); + //close MBE out files if (opts->mbe_out_f != NULL) closeMbeOutFile (opts, state); if (opts->mbe_out_fR != NULL) closeMbeOutFileR (opts, state); @@ -2611,6 +2616,50 @@ main (int argc, char **argv) opts.audio_in_type = 0; } + //UDP Socket Blaster Audio Output Setup + if((strncmp(opts.audio_out_dev, "udp", 3) == 0)) + { + + //read in values + fprintf (stderr, "UDP Blaster Output: "); + char * curr; + + curr = strtok(opts.audio_out_dev, ":"); //should be 'udp' + if (curr != NULL) ; //continue + else goto UDPEND; //end early with preset values + + curr = strtok(NULL, ":"); //udp blaster hostname + if (curr != NULL) + strncpy (opts.udp_hostname, curr, 1023); //set address to blast to + + curr = strtok(NULL, ":"); //udp blaster port + if (curr != NULL) + opts.udp_portno = atoi (curr); + + UDPEND: + fprintf (stderr, "%s:", opts.udp_hostname); + fprintf (stderr, "%d \n", opts.udp_portno); + + int err = udp_socket_connect(&opts, &state); + if (err < 0) + { + fprintf (stderr, "Error Configuring UDP Socket for UDP Blaster Audio :( \n"); + #ifdef AERO_BUILD + sprintf (opts.audio_in_dev, "%s", "/dev/dsp"); + opts.audio_in_type = 5; + //since I can't determine what the configuration will be for 48k1 or 8k2 here(lazy), need to exit + exitflag = 1; + #else + sprintf (opts.audio_out_dev, "%s", "pulse"); + opts.audio_out_type = 0; + #endif + + } + + opts.audio_out_type = 8; + + } + if((strncmp(opts.audio_out_dev, "pulse", 5) == 0)) { opts.audio_out_type = 0; diff --git a/src/dsd_ncurses.c b/src/dsd_ncurses.c index 4f413b1..dd030b5 100644 --- a/src/dsd_ncurses.c +++ b/src/dsd_ncurses.c @@ -185,6 +185,22 @@ void beeper (dsd_opts * opts, dsd_state * state, int lr) } + else if (opts->audio_out_type == 8) //UDP Audio + { + if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) + udp_socket_blaster (opts, state, 320*4, samp_fs); + + if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 1) + udp_socket_blaster (opts, state, 160*4, samp_f); + + if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 0) + udp_socket_blaster (opts, state, 320*2, samp_ss); + + if (opts->pulse_digi_out_channels == 1 && opts->floating_point == 0) + udp_socket_blaster (opts, state, 160*2, samp_s); + + } + else if (opts->audio_out_type == 1) //STDOUT { if (opts->pulse_digi_out_channels == 2 && opts->floating_point == 1) @@ -454,10 +470,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) sf_close(opts->tcp_file_in); } - if (opts->audio_in_type == 5) //close UDP input SF file so we don't buffer audio while not decoding - { - // sf_close(opts->udp_file_in); //disable for testing - } state->payload_keyid = 0; state->payload_keyidR = 0; @@ -1810,10 +1822,6 @@ void ncursesMenu (dsd_opts * opts, dsd_state * state) opts->tcp_file_in = sf_open_fd(opts->tcp_sockfd, SFM_READ, opts->audio_in_file_info, 0); } - if (opts->audio_in_type == 5) //re-open UDP input 'file' - { - // opts->udp_file_in = sf_open_fd(opts->udp_sockfd, SFM_READ, opts->audio_in_file_info, 0); - } //update sync time on cc sync so we don't immediately go CC hunting when exiting the menu state->last_cc_sync_time = time(NULL); @@ -2346,6 +2354,18 @@ ncursesPrinter (dsd_opts * opts, dsd_state * state) printw (" \n"); } + if (opts->audio_out_type == 8) + { + printw ("| UDP Audio Output: %s:%d; %d kHz %d Ch; %02.0f%%", opts->udp_hostname, opts->udp_portno, opts->pulse_digi_rate_out/1000, opts->pulse_digi_out_channels, state->aout_gain*2); + if (opts->pulse_digi_out_channels == 2) printw (" G: %02.0f%%", state->aout_gainR*2); + if (opts->audio_gain == 0) printw (" (+/-) Auto"); + if (opts->audio_gain > 0) printw (" (+/-) Manual"); + if (opts->call_alert == 1) printw (" *CA!"); //Call Alert + if ( (opts->audio_out_type == 5 && opts->pulse_digi_rate_out == 48000 && opts->pulse_digi_out_channels == 1) && (opts->frame_provoice == 1 || opts->monitor_input_audio == 1) ) + printw (" - Monitor RMS: %04ld ", opts->rtl_rms); + printw (" \n"); + } + // if (opts->monitor_input_audio == 1) // { // printw ("| Monitoring Source Audio when Carrier Present and No Sync Detected (WIP)\n"); diff --git a/src/dsd_rigctl.c b/src/dsd_rigctl.c index 8f21c06..24ba02a 100644 --- a/src/dsd_rigctl.c +++ b/src/dsd_rigctl.c @@ -30,6 +30,8 @@ void error(char *msg) { exit(0); } +struct sockaddr_in address; + // // Connect // @@ -281,42 +283,58 @@ void rtl_udp_tune(dsd_opts * opts, dsd_state * state, long int frequency) close (handle); //close socket after sending. } -// int udp_sock_con(dsd_opts * opts, dsd_state * state) -// { -// UNUSED(state); -// UNUSED(opts); +int udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data) +{ + UNUSED(state); + size_t err = 0; -// int handle, err; //convert handle and address to state variables -// handle = err = 0; -// unsigned short udp_port = 9999; -// struct sockaddr_in address; -// handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); -// if (handle != 0) -// { -// fprintf (stderr, " UDP Socket Error \n"); -// return (handle); -// } -// memset((char * ) & address, 0, sizeof(address)); -// address.sin_family = AF_INET; -// err = address.sin_addr.s_addr = inet_addr("127.0.0.1"); //make user configurable later -// if (err != 0) -// { -// fprintf (stderr, " UDP inet_addr Error \n"); -// return (err); -// } -// address.sin_port = htons(udp_port); -// if (err != 0) -// { -// fprintf (stderr, " UDP htons Error \n"); -// return (err); -// } -// // close (handle); //close socket after sending. //leave open for the blaster -// } + //listen with: -// int udp_sock_send(dsd_opts * opts, dsd_state * state, short samp[320]) -// { -// UNUSED(state); -// UNUSED(opts); + //short 8k/2 + //socat stdio udp-listen:23456 | play -q -b 16 -r 8000 -c2 -t s16 - -// sendto(handle, samp, 320, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in)); -// } \ No newline at end of file + //short 48k/2 + //socat stdio udp-listen:23456 | play -q -b 16 -r 48000 -c2 -t s16 - + + //float 8k/2 + //socat stdio udp-listen:23456 | play -q -e float -b 32 -r 8000 -c2 -t f32 - + + //float 8k/1 + //socat stdio udp-listen:23456 | play -q -e float -b 32 -r 8000 -c1 -t f32 - + + //send audio or data to socket + err = sendto(opts->udp_sockfd, data, nsam, 0, (const struct sockaddr * ) & address, sizeof(struct sockaddr_in)); + if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %d", err); //return value here is size_t number of characters sent, or -1 for failure + if (err < nsam) fprintf (stderr, "\n UDP Underflow %d", err); //I'm not even sure if this is possible +} + +int udp_socket_connect(dsd_opts * opts, dsd_state * state) +{ + UNUSED(state); + UNUSED(opts); + + int handle, err; //convert handle and address to state variables + handle = err = 0; + unsigned short udp_port = opts->udp_portno; + opts->udp_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (opts->udp_sockfd < 0) //is this correct? + { + fprintf (stderr, " UDP Socket Error \n"); + return (handle); + } + memset((char * ) & address, 0, sizeof(address)); + address.sin_family = AF_INET; + err = address.sin_addr.s_addr = inet_addr(opts->udp_hostname); + if (err < 0) + { + fprintf (stderr, " UDP inet_addr Error \n"); + return (err); + } + address.sin_port = htons(udp_port); + if (err < 0) + { + fprintf (stderr, " UDP htons Error \n"); + return (err); + } + +}