From b152e10d40c144b1f9c2cbc00357f2f4655c075c Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sun, 24 Dec 2023 22:19:35 -0500 Subject: [PATCH 1/7] Update build --- build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build b/build index 5eb20089..29cc0ac3 100755 --- a/build +++ b/build @@ -1,5 +1,10 @@ #!/bin/sh F=$@ +if [ "$F" = "" ]; then + echo "No build target provided" + echo "Assuming target is sbitx" + F=sbitx +fi WORKING_DIRECTORY=`pwd` echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" date From 66cc11eeb8c385edf85ab49664980ddf77f461ab Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Wed, 27 Dec 2023 19:21:14 -0500 Subject: [PATCH 2/7] Update sbitx_sound.c Fix for high CPU usage and audio dropouts. Includes a define to enable debugging messages. --- sbitx_sound.c | 384 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 310 insertions(+), 74 deletions(-) diff --git a/sbitx_sound.c b/sbitx_sound.c index ebaea28d..a7ec32a0 100644 --- a/sbitx_sound.c +++ b/sbitx_sound.c @@ -3,11 +3,18 @@ #include #include #include +#include #include #include "sound.h" #include "wiringPi.h" #include "sdr.h" +// Set the DEBUG define to 1 to compile in the debugging messages. +// Set the DEBUG define to 2 to compile in detailed error reporting debugging messages. +#define DEBUG 0 + +// Set the DISABLE_LOOPBACK define to 1 to disable the loopback processing. +#define DISABLE_LOOPBACK 0 /* follows the tutorial at http://alsamodular.sourceforge.net/alsa_programming_howto.html Next thing to try is http://www.saunalahti.fi/~s7l/blog/2005/08/21/Full%20Duplex%20ALSA @@ -23,6 +30,13 @@ Next thing to try is http://www.saunalahti.fi/~s7l/blog/2005/08/21/Full%20Duplex configuring the Raspberry Pi with Wolfson codec. */ +/* + The audio channels are: + PCM Playback + Loopback Capture + PCM Capture + Loopback Play +*/ /* MIXER api @@ -128,7 +142,7 @@ void sound_mixer(char *card_name, char *element, int make_on) } int rate = 96000; /* Sample rate */ -static snd_pcm_uframes_t buff_size = 8192; /* Periodsize (bytes) */ +static snd_pcm_uframes_t buff_size = 8192; /* Periodsize (bytes) */ static int n_periods_per_buffer = 2; /* Number of periods */ //static int n_periods_per_buffer = 1024; /* Number of periods */ @@ -150,8 +164,10 @@ static int sound_thread_continue = 0; pthread_t sound_thread, loopback_thread; #define LOOPBACK_LEVEL_DIVISOR 8 // Constant used to reduce audio level to the loopback channel (FLDIGI) -static int play_write_error = 0; // count play channel write errors -static int loopback_write_error = 0; // count loopback channel write errors +static int pcm_capture_error = 0; // count pcm capture errors +static int pcm_play_write_error = 0; // count play channel write errors +static int pcm_loopback_write_error = 0; // count loopback channel write errors +static int result = 0; // scratch variable for storing function call results // Note: Error messages appear when the sbitx program is started from the command line int use_virtual_cable = 0; @@ -169,23 +185,29 @@ The sequence of the alsa functions must be maintained for this to work consisten It returns a -1 if the device didn't open. The error message is on stderr. IMPORTANT: -The sound is playback is carried on in a non-blocking way +The sound is playback is carried on in a non-blocking way +Update - Sound playback now uses blocking ALSA calls, although the calls +do not block until the buffers are completely filled (which will never occur) */ int sound_start_play(char *device){ //found out the correct device through aplay -L (for pcm devices) - - snd_pcm_hw_params_alloca(&hwparams); //more alloc - //puts a playback handle into the pointer to the pointer - int e = snd_pcm_open(&pcm_play_handle, device, play_stream, SND_PCM_NONBLOCK); + +#if DEBUG > 0 + printf ("opening audio playback stream to %s\n", device); +#endif + int e = snd_pcm_open(&pcm_play_handle, device, play_stream, 0); // was SND_PCM_NONBLOCK if (e < 0) { fprintf(stderr, "Error opening PCM playback device %s: %s\n", device, snd_strerror(e)); return -1; } + snd_pcm_hw_params_alloca(&hwparams); // more alloc + snd_pcm_sw_params_alloca(&swparams); // more alloc + //fills up the hwparams with values, hwparams was allocated above e = snd_pcm_hw_params_any(pcm_play_handle, hwparams); @@ -193,7 +215,7 @@ int sound_start_play(char *device){ fprintf(stderr, "*Error getting hw playback params (%d)\n", e); return(-1); } - + // set the pcm access to interleaved e = snd_pcm_hw_params_set_access(pcm_play_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { @@ -219,9 +241,10 @@ int sound_start_play(char *device){ } if (rate != exact_rate) fprintf(stderr, "*The playback rate %d changed to %d Hz\n", rate, exact_rate); -/* else +#if DEBUG > 0 + else fprintf(stderr, "Playback sampling rate is set to %d\n", exact_rate); -*/ +#endif /* Set number of channels */ if ((e = snd_pcm_hw_params_set_channels(pcm_play_handle, hwparams, 2)) < 0) { @@ -229,7 +252,8 @@ int sound_start_play(char *device){ return(-1); } - +/* +// This function call and the next have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 // frame = bytes_per_sample x n_channel // period = frames transfered at a time (160 for voip, etc.) // we use two periods per buffer. @@ -237,22 +261,50 @@ int sound_start_play(char *device){ fprintf(stderr, "*Error setting playback periods.\n"); return(-1); } - +*/ // the buffer size is each periodsize x n_periods - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; - //printf("trying for buffer size of %ld\n", n_frames); + // snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8*4; // A Larger buffer - N3SB Hack +#if DEBUG > 0 + printf("trying for buffer size of %ld\n", n_frames); +#endif +/* +// This function call and the previous have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 e = snd_pcm_hw_params_set_buffer_size_near(pcm_play_handle, hwparams, &n_frames); if (e < 0) { fprintf(stderr, "*Error setting playback buffersize.\n"); return(-1); } +*/ + // This function call replaces the two function calls above - N3SB December 2023 + e = snd_pcm_hw_params_set_period_size_near(pcm_play_handle, hwparams, &n_frames, 0); + if (e < 0) { + fprintf(stderr, "*Error setting playback buffersize.\n"); + return(-1); + } if (snd_pcm_hw_params(pcm_play_handle, hwparams) < 0) { fprintf(stderr, "*Error setting playback HW params.\n"); return(-1); } -// puts("All hw params set to play sound"); + + // get the current swparams + e = snd_pcm_sw_params_current(pcm_play_handle, swparams); + if (e < 0) { + printf("Unable to determine current swparams for playback: %s\n", snd_strerror(e)); + } + + e = snd_pcm_sw_params_set_start_threshold(pcm_play_handle, swparams, (8192) ); + if (e < 0) { + printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(e)); + } + + +#if DEBUG > 0 + printf("PCM Playback Buffer Size: %d\n",snd_pcm_avail(pcm_play_handle)); + puts("All hw params set to play sound"); +#endif return 0; } @@ -261,7 +313,10 @@ int sound_start_play(char *device){ int sound_start_loopback_capture(char *device){ snd_pcm_hw_params_alloca(&hloop_params); - //printf ("opening audio tx stream to %s\n", device); + +#if DEBUG > 0 + printf ("opening audio loopback tx stream to %s\n", device); +#endif int e = snd_pcm_open(&loopback_capture_handle, device, capture_stream, 0); if (e < 0) { @@ -308,22 +363,31 @@ int sound_start_loopback_capture(char *device){ return(-1); } +/* //printf("%d: set the #channels\n", __LINE__, 2); - /* Set number of periods. Periods used to be called fragments. */ + // Set number of periods. Periods used to be called fragments. if ((e = snd_pcm_hw_params_set_periods(loopback_capture_handle, hloop_params, n_periods_per_buffer, 0)) < 0) { fprintf(stderr, "*Error setting loopback capture periods.\n"); return(-1); } // the buffer size is each periodsize x n_periods - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; - //printf("trying for buffer size of %ld\n", n_frames); + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer) / 8; + // printf("trying for buffer size of %ld\n", n_frames); e = snd_pcm_hw_params_set_buffer_size_near(loopback_capture_handle, hloop_params, &n_frames); if (e < 0) { fprintf(stderr, "*Error setting loopback capture buffersize.\n"); return(-1); } - +*/ + // This function call and the previous have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer) / 8; + e = snd_pcm_hw_params_set_period_size_near(loopback_capture_handle, hloop_params, &n_frames, 0); + if (e < 0) { + fprintf(stderr, "*Error setting loopback capture buffersize.\n"); + return(-1); + } + //printf("%d: set buffer to \n", __LINE__, n_frames); if (snd_pcm_hw_params(loopback_capture_handle, hloop_params) < 0) { fprintf(stderr, "*Error setting capture HW params.\n"); @@ -343,9 +407,14 @@ int sound_start_loopback_capture(char *device){ } if ((e = snd_pcm_sw_params_set_stop_threshold(loopback_capture_handle, sloop_params, 1)) < 0){ - fprintf(stderr, "Unable to set stop threshold for loopback capture\n"); } + +#if DEBUG > 0 + printf("Loopback Capture Buffer Size: %d\n",snd_pcm_avail(loopback_capture_handle)); + puts("All hw params set for loopback capture sound"); +#endif + return 0; } @@ -359,7 +428,10 @@ just wait for the next block to arrive int sound_start_capture(char *device){ snd_pcm_hw_params_alloca(&hwparams); - + +#if DEBUG > 0 + printf ("opening PCM Capture stream to %s\n", device); +#endif int e = snd_pcm_open(&pcm_capture_handle, device, capture_stream, 0); if (e < 0) { @@ -370,65 +442,77 @@ int sound_start_capture(char *device){ e = snd_pcm_hw_params_any(pcm_capture_handle, hwparams); if (e < 0) { - fprintf(stderr, "*Error setting capture access (%d)\n", e); + fprintf(stderr, "*Error setting PCM capture access (%d)\n", e); return(-1); } e = snd_pcm_hw_params_set_access(pcm_capture_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { - fprintf(stderr, "*Error setting capture access.\n"); + fprintf(stderr, "*Error setting PCM capture access.\n"); return(-1); } /* Set sample format */ e = snd_pcm_hw_params_set_format(pcm_capture_handle, hwparams, SND_PCM_FORMAT_S32_LE); if (e < 0) { - fprintf(stderr, "*Error setting capture format.\n"); + fprintf(stderr, "*Error setting PCM capture format.\n"); return(-1); } - /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_rate = rate; e = snd_pcm_hw_params_set_rate_near(pcm_capture_handle, hwparams, &exact_rate, 0); if ( e< 0) { - fprintf(stderr, "*Error setting capture rate.\n"); + fprintf(stderr, "*Error setting PCM capture rate.\n"); return(-1); } if (rate != exact_rate) - fprintf(stderr, "#The capture rate %d changed to %d Hz\n", rate, exact_rate); + fprintf(stderr, "#The PCM capture rate %d changed to %d Hz\n", rate, exact_rate); /* Set number of channels */ if ((e = snd_pcm_hw_params_set_channels(pcm_capture_handle, hwparams, 2)) < 0) { - fprintf(stderr, "*Error setting capture channels.\n"); + fprintf(stderr, "*Error setting PCM capture channels.\n"); return(-1); } - - /* Set number of periods. Periods used to be called fragments. */ +/* + // Set number of periods. Periods used to be called fragments. if ((e = snd_pcm_hw_params_set_periods(pcm_capture_handle, hwparams, n_periods_per_buffer, 0)) < 0) { fprintf(stderr, "*Error setting capture periods.\n"); return(-1); } - // the buffer size is each periodsize x n_periods snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; //printf("trying for buffer size of %ld\n", n_frames); - e = snd_pcm_hw_params_set_buffer_size_near(pcm_play_handle, hwparams, &n_frames); + e = snd_pcm_hw_params_set_buffer_size_near(pcm_capture_handle, hwparams, &n_frames); if (e < 0) { - fprintf(stderr, "*Error setting capture buffersize.\n"); + fprintf(stderr, "*Error setting PCM capture buffersize.\n"); return(-1); } +*/ + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; + // This function call replaces the two function calls above - N3SB December 2023 + e = snd_pcm_hw_params_set_period_size_near(pcm_capture_handle, hwparams, &n_frames, 0); + if (e < 0) { + fprintf(stderr, "*Error setting PCM Capture buffersize.\n"); + return(-1); + } + if (snd_pcm_hw_params(pcm_capture_handle, hwparams) < 0) { - fprintf(stderr, "*Error setting capture HW params.\n"); + fprintf(stderr, "*Error setting PCM capture HW params.\n"); return(-1); } +#if DEBUG > 0 + printf("Capture Buffer Size: %d\n",snd_pcm_avail(pcm_capture_handle)); + puts("All hw params set for PCM sound capture"); +#endif + return 0; } @@ -437,8 +521,10 @@ int sound_start_loopback_play(char *device){ snd_pcm_hw_params_alloca(&hwparams); //more alloc - //printf ("opening audio rx stream to %s\n", device); - int e = snd_pcm_open(&loopback_play_handle, device, play_stream, SND_PCM_NONBLOCK); +#if DEBUG > 0 + printf ("opening Loopback Play stream to %s\n", device); +#endif + int e = snd_pcm_open(&loopback_play_handle, device, play_stream, 0); // was SND_PCM_NONBLOCK if (e < 0) { fprintf(stderr, "Error opening loopback playback device %s: %s\n", device, snd_strerror(e)); @@ -454,14 +540,14 @@ int sound_start_loopback_play(char *device){ e = snd_pcm_hw_params_set_access(loopback_play_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { - fprintf(stderr, "*Error setting loopback access.\n"); + fprintf(stderr, "*Error setting loopback Play access.\n"); return(-1); } /* Set sample format */ e = snd_pcm_hw_params_set_format(loopback_play_handle, hwparams, SND_PCM_FORMAT_S32_LE); if (e < 0) { - fprintf(stderr, "*Error setting loopback format.\n"); + fprintf(stderr, "*Error setting loopback Play format.\n"); return(-1); } @@ -483,7 +569,7 @@ int sound_start_loopback_play(char *device){ return(-1); } - +/* // frame = bytes_per_sample x n_channel // period = frames transfered at a time (160 for voip, etc.) // we use two periods per buffer. @@ -492,7 +578,6 @@ int sound_start_loopback_play(char *device){ return(-1); } - // the buffer size is each periodsize x n_periods snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; //lets pump it up to see if we can reduce the dropped frames @@ -503,14 +588,27 @@ int sound_start_loopback_play(char *device){ fprintf(stderr, "*Error setting loopback playback buffersize.\n"); return(-1); } - - //printf("loopback playback buffer size is set to %d\n", n_frames); +*/ + // This function call replaces the two function calls above - N3SB December 2023 + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; + //lets pump it up to see if we can reduce the dropped frames + n_frames *= 4; + e = snd_pcm_hw_params_set_period_size_near(loopback_play_handle, hwparams, &n_frames, 0); + if (e < 0) { + fprintf(stderr, "*Error setting loopback play buffersize.\n"); + return(-1); + } if (snd_pcm_hw_params(loopback_play_handle, hwparams) < 0) { fprintf(stderr, "*Error setting loopback playback HW params.\n"); return(-1); } +#if DEBUG > 0 + printf("Loopback Playback Buffer Size: %d\n",snd_pcm_avail(pcm_capture_handle)); + puts("All hw params set for Loopback Playback"); +#endif + return 0; } @@ -543,6 +641,20 @@ static int nframes = 0; int32_t resample_in[10000]; int32_t resample_out[10000]; +struct timeval GetTimeStamp() +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return tv; +} +struct timeval tv; +signed long pcm_read_old_time = 0; +signed long pcm_read_new_time = 0; +signed long delta_time = 0; +unsigned long samples_read = 0; +unsigned long samples_written = 0; +static unsigned long loop_counter = 0; + int last_second = 0; int nsamples = 0; int played_samples = 0; @@ -552,6 +664,8 @@ int sound_loop(){ int pcmreturn, i, j, loopreturn; short s1, s2; int frames; + int pcm_read_avail; + int pcm_write_avail; //we allocate enough for two channels of int32_t sized samples data_in = (int32_t *)malloc(buff_size * 2); @@ -564,14 +678,26 @@ int sound_loop(){ output_q = (int32_t *)malloc(buff_size * 2); frames = buff_size / 8; - + + snd_pcm_prepare(pcm_play_handle); snd_pcm_prepare(loopback_play_handle); - snd_pcm_writei(pcm_play_handle, data_out, frames); - snd_pcm_writei(pcm_play_handle, data_out, frames); +/* + pcmreturn = snd_pcm_writei(pcm_play_handle, data_out, frames*2); // Get a head start on filling the queue +#if DEBUG > 0 + printf("Pre-filling play and loopback queues\n"); + printf("Playback buffer filled with %d samples\n",pcmreturn); +#endif + pcmreturn = snd_pcm_writei(loopback_play_handle, data_out, frames); // Get a head start on filling the queue +#if DEBUG > 0 + printf("Loopback buffer filled with %d samples\n",pcmreturn); +#endif +*/ //Note: the virtual cable samples queue should be flushed at the start of tx qloop.stall = 1; + +// ******************************************************************************************************** The Big Loop starts here while(sound_thread_continue) { @@ -579,21 +705,51 @@ int sound_loop(){ //this is opened as a blocking device, hence we derive accurate timing last_time = gettime_now.tv_nsec/1000; + + // printf("%d\n", last_time); + pcm_read_avail = snd_pcm_avail(pcm_capture_handle); + + tv = GetTimeStamp(); // get time + pcm_read_new_time= 1000000 * tv.tv_sec + tv.tv_usec; // Store time in microseconds + delta_time = pcm_read_new_time - pcm_read_old_time; + if ((delta_time > 11667) || (delta_time < 9667)) // Loop should iterate every 10667 microseconds + { +#if DEBUG > 1 + printf("Loop Counter: %d, Loop Time: %d, Samples available: %d\n", loop_counter, delta_time, pcm_read_avail); +#endif + } + pcm_read_old_time = pcm_read_new_time; + - - while ((pcmreturn = snd_pcm_readi(pcm_capture_handle, data_in, frames)) < 0){ - snd_pcm_prepare(pcm_capture_handle); - //putchar('='); + while ((pcmreturn = snd_pcm_readi(pcm_capture_handle, data_in, frames)) < 0) + { + result = snd_pcm_prepare(pcm_capture_handle); +#if DEBUG > 0 + printf("**** PCM Capture Error: %s count = %d\n",snd_strerror(pcmreturn), pcm_capture_error++); +#endif } +#if DEBUG > 1 + printf("Delta Time: %d, Available output sample storage: %d\n", delta_time, snd_pcm_avail(pcm_play_handle)); +#endif + samples_read += pcmreturn; + i = 0; j = 0; +#if DEBUG > 0 + if (pcmreturn < 1024) + printf("\n----PCM Read Size = %d\n",pcmreturn); +#endif int ret_card = pcmreturn; - if (use_virtual_cable){ + if (use_virtual_cable) + { //printf(" we have %d in qloop, writing now\n", q_length(&qloop)); // if don't we have enough to last two iterations loop back... - if (q_length(&qloop) < pcmreturn){ -// puts(" skipping"); + if (q_length(&qloop) < pcmreturn) + { +#if DEBUG > -1 + puts(" skipping\n"); +#endif continue; } @@ -608,7 +764,7 @@ int sound_loop(){ j++; } played_samples += 1024; - } + } // end for use_virtual_cable test else { while (i < ret_card){ input_i[i] = data_in[j++]/2; @@ -617,7 +773,7 @@ int sound_loop(){ } } - //printf("%d %ld %d\n", count++, nsamples, pcmreturn); + // printf("\n-%d %ld %d\n", count++, nsamples, pcmreturn); sound_process(input_i, input_q, output_i, output_q, ret_card); @@ -628,19 +784,55 @@ int sound_loop(){ data_out[j++] = output_q[i++]; } - int framesize = ret_card; int offset = 0; int play_write_errors = 0; + int pswitch = 0; while(framesize > 0) { - pcmreturn = snd_pcm_writei(pcm_play_handle, data_out + offset, framesize); - if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors + do + { + pcmreturn = snd_pcm_avail(pcm_play_handle); + } while ((pcmreturn == 0) || (pcmreturn == -11)); + + do + { + pcmreturn = snd_pcm_writei(pcm_play_handle, data_out + offset, framesize); + } while (pcmreturn == -11); + + if ((pcmreturn > 0) && (pcmreturn < 1024)) + { +#if DEBUG > 0 + printf("#### Partial Write ####"); // would signify that the playback channel didn't accept all the samples +#endif + } + // if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors + if(pcmreturn < 0) { // Handle an error condition from the snd_pcm_writei function -// printf("Play PCM Write Error: %s count = %d\n",snd_strerror(pcmreturn), play_write_error++); - snd_pcm_prepare(pcm_play_handle); +#if DEBUG > 0 + printf("Loop Counter: %d, Play PCM Write Error %d: %s count = %d\n",loop_counter, pcmreturn, snd_strerror(pcmreturn), pcm_play_write_error++); +#endif + if (pcmreturn == -EPIPE) + { +#if DEBUG > 0 + printf("Samples Read: %d, Samples Written: %d, delta: %d, available %d\n", samples_read, samples_written, samples_read - samples_written, pcm_write_avail); + printf("Available write buffer: %d\n", pcm_write_avail); +#endif + snd_pcm_recover(pcm_play_handle, pcmreturn, 0); + } + + +#if DEBUG > 0 + printf("Now Available write buffer: %d\n", snd_pcm_avail(pcm_play_handle)); +#endif + +#if DEBUG <2 + // snd_pcm_recover(pcm_play_handle, pcmreturn, 1); // Does not provide detailed error message +#else + // snd_pcm_recover(pcm_play_handle, pcmreturn, 0); // Provides detailed error message +#endif } if(pcmreturn >= 0) @@ -648,12 +840,37 @@ int sound_loop(){ // Calculate remaining number of samples to be sent and new position in sample array. // If all the samples were processed by the snd_pcm_writei function then framesize will be // zero and the while() loop will end. + if (pswitch == 1) + { +#if DEBUG > 0 + printf("%d ",pcmreturn); +#endif + } framesize -= pcmreturn; + if ((framesize > 0) && (pswitch == 0)) + { +#if DEBUG > 0 + printf("%d ",pcmreturn); +#endif + pswitch = 1; + } offset += (pcmreturn * 2); + samples_written += pcmreturn; + } + if (framesize == 0) + { + if (pswitch == 1) + { +#if DEBUG > 0 + printf("\n"); +#endif + pswitch = 0; + } } } // End of new pcm play write routine +#if DISABLE_LOOPBACK == 0 //decimate the line out to half, ie from 96000 to 48000 //play the received data (from left channel) to both of line out @@ -675,15 +892,30 @@ int sound_loop(){ while(framesize > 0) { + do + { + pcmreturn = snd_pcm_avail(loopback_play_handle); + } while ((pcmreturn == 0) || (pcmreturn == -11)); + // printf("Writing %d frame to loopback\n", framesize); + + do + { pcmreturn = snd_pcm_writei(loopback_play_handle, line_out + offset, framesize); + } while (pcmreturn == -11); + + // if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors if(pcmreturn < 0) - { - //printf("Loopback PCM Write %d bytes Error: %s count = %d\n", - // framesize, snd_strerror(pcmreturn), loopback_write_error++); - // Handle an error condition from the snd_pcm_writei function - play_write_errors++; - snd_pcm_prepare(loopback_play_handle); + { // Handle an error condition from the snd_pcm_writei function +#if DEBUG > 0 + printf("Loopback PCM Write %d bytes Error %d: %s count = %d\n", framesize, pcmreturn, snd_strerror(pcmreturn), pcm_loopback_write_error++); +#endif + +#if DEBUG <2 + snd_pcm_recover(loopback_play_handle, pcmreturn, 1); // Does not provide detailed error message +#else + snd_pcm_recover(loopback_play_handle, pcmreturn, 0); // Provides detailed error message +#endif } if(pcmreturn >= 0) @@ -693,17 +925,21 @@ int sound_loop(){ // zero and the while() loop will end. framesize -= pcmreturn; offset += (pcmreturn * 2); + if (framesize > 0) + { +#if DEBUG > 0 + printf("\nLoopback - pcmreturn = %d\n", pcmreturn); +#endif + } } } // End of new pcm loopback write routine - - if(play_write_errors > 20){ -// printf("play write error!\n"); - play_write_errors = 0; - } + +#endif //played_samples += pcmreturn; - } + loop_counter++; + } // End of while (sound_thread_continue) loop //fclose(pf); printf("********Ending sound thread\n"); } @@ -723,7 +959,7 @@ int loopback_loop(){ while(sound_thread_continue) { - //restart the pcm capture if there is an error reading the samples + //restart the loopback capture if there is an error reading the samples //this is opened as a blocking device, hence we derive accurate timing last_time = gettime_now.tv_nsec/1000; From 1bbf6a4ee071830a0fb3011ed7008da8b5185169 Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sun, 31 Dec 2023 16:28:28 -0500 Subject: [PATCH 3/7] Update fft_filter.c - change level of rigor and add Wisdom file support Changed the level of rigor from FFTW_ESTIMATE to FFTW_MEASURE in order to resolve an issue where the FFTW library would sometime make a bad choice about the FFT algorithm to use. Added support for Wisdom files so that the FFTW library does not have to spend time picking the algorithm. --- fft_filter.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/fft_filter.c b/fft_filter.c index fdf9ccd2..8978b8bd 100644 --- a/fft_filter.c +++ b/fft_filter.c @@ -11,6 +11,15 @@ #include #include "sdr.h" +// Wisdom Defines for the FFTW and FFTWF libraries +// Options for WISDOM_MODE from least to most rigorous are FFTW_ESTIMATE, FFTW_MEASURE, FFTW_PATIENT, and FFTW_EXHAUSTIVE +// The FFTW_ESTIMATE mode seems to make completely incorrect Wisdom plan choices sometimes, and is not recommended. +// Wisdom plans found in an existing Wisdom file will negate the need for time consuming Wisdom plan calculations +// if the Wisdom plans in the file were generated at the same or more rigorous level. +#define WISDOM_MODE FFTW_MEASURE +#define PLANTIME -1 // spend no more than plantime seconds finding the best FFT algorithm. -1 turns the platime cap off. +char wisdom_file_f[] = "sbitx_wisdom_f.wis"; + // Modified Bessel function of the 0th kind, used by the Kaiser window const float i0(float const z){ const float t = (z*z)/4; @@ -86,8 +95,17 @@ int window_filter(int const L,int const M,complex float * const response,float c // fftw_plan can overwrite its buffers, so we're forced to make a temp. Ugh. complex float * const buffer = fftwf_alloc_complex(N); - fftwf_plan fwd_filter_plan = fftwf_plan_dft_1d(N,buffer,buffer,FFTW_FORWARD,FFTW_ESTIMATE); - fftwf_plan rev_filter_plan = fftwf_plan_dft_1d(N,buffer,buffer,FFTW_BACKWARD,FFTW_ESTIMATE); + + fftw_set_timelimit(PLANTIME); + fftwf_set_timelimit(PLANTIME); + int e = fftwf_import_wisdom_from_filename(wisdom_file_f); + if (e == 0) + { + printf("Generating Wisdom File...\n"); + } + fftwf_plan fwd_filter_plan = fftwf_plan_dft_1d(N,buffer,buffer,FFTW_FORWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + fftwf_plan rev_filter_plan = fftwf_plan_dft_1d(N,buffer,buffer,FFTW_BACKWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + fftwf_export_wisdom_to_filename(wisdom_file_f); // Convert to time domain memcpy(buffer,response,N*sizeof(*buffer)); From ecd4fc8350df6bd066a052fdf2728306074196a1 Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sun, 31 Dec 2023 16:31:23 -0500 Subject: [PATCH 4/7] Update sbitx.c - change level of rigor and add Wisdom file support Changed the level of rigor from FFTW_ESTIMATE to FFTW_MEASURE in order to resolve an issue where the FFTW library would sometimes make a bad choice about the FFT algorithm to use. Added support for Wisdom files so that the FFTW library does not have to spend time picking the algorithm. --- sbitx.c | 76 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/sbitx.c b/sbitx.c index a4eaea8c..6809ec56 100644 --- a/sbitx.c +++ b/sbitx.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -22,6 +22,8 @@ #include "si5351.h" #include "ini.h" +#define DEBUG 0 + char audio_card[32]; static int tx_shift = 512; @@ -51,6 +53,15 @@ float spectrum_window[MAX_BINS]; void set_rx1(int frequency); void tr_switch(int tx_on); +// Wisdom Defines for the FFTW and FFTWF libraries +// Options for WISDOM_MODE from least to most rigorous are FFTW_ESTIMATE, FFTW_MEASURE, FFTW_PATIENT, and FFTW_EXHAUSTIVE +// The FFTW_ESTIMATE mode seems to make completely incorrect Wisdom plan choices sometimes, and is not recommended. +// Wisdom plans found in an existing Wisdom file will negate the need for time consuming Wisdom plan calculations +// if the Wisdom plans in the file were generated at the same or more rigorous level. +#define WISDOM_MODE FFTW_MEASURE +#define PLANTIME -1 // spend no more than plantime seconds finding the best FFT algorithm. -1 turns the platime cap off. +char wisdom_file[] = "sbitx_wisdom.wis"; + fftw_complex *fft_out; // holds the incoming samples in freq domain (for rx as well as tx) fftw_complex *fft_in; // holds the incoming samples in time domain (for rx as well as tx) fftw_complex *fft_m; // holds previous samples for overlap and discard convolution @@ -130,12 +141,12 @@ void radio_tune_to(u_int32_t f){ } void fft_init(){ - int mem_needed; + // int mem_needed; //printf("initializing the fft\n"); fflush(stdout); - mem_needed = sizeof(fftw_complex) * MAX_BINS; + // mem_needed = sizeof(fftw_complex) * MAX_BINS; fft_m = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS/2); fft_in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS); @@ -147,8 +158,16 @@ void fft_init(){ memset(fft_out, 0, sizeof(fftw_complex) * MAX_BINS); memset(fft_m, 0, sizeof(fftw_complex) * MAX_BINS/2); - plan_fwd = fftw_plan_dft_1d(MAX_BINS, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); - plan_spectrum = fftw_plan_dft_1d(MAX_BINS, fft_in, fft_spectrum, FFTW_FORWARD, FFTW_ESTIMATE); + fftw_set_timelimit(PLANTIME); + fftwf_set_timelimit(PLANTIME); + int e = fftw_import_wisdom_from_filename(wisdom_file); + if (e == 0) + { + printf("Generating Wisdom File...\n"); + } + plan_fwd = fftw_plan_dft_1d(MAX_BINS, fft_in, fft_out, FFTW_FORWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + plan_spectrum = fftw_plan_dft_1d(MAX_BINS, fft_in, fft_spectrum, FFTW_FORWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + fftw_export_wisdom_to_filename(wisdom_file); //zero up the previous 'M' bins for (int i= 0; i < MAX_BINS/2; i++){ @@ -240,18 +259,24 @@ void set_lpf_40mhz(int frequency){ lpf = LPF_A; if (lpf == prev_lpf){ - //puts("LPF not changed"); +#if DEBUG > 0 + puts("LPF not changed"); +#endif return; } - - //printf("##################Setting LPF to %d\n", lpf); + +#if DEBUG > 0 + printf("##################Setting LPF to %d\n", lpf); +#endif digitalWrite(LPF_A, LOW); digitalWrite(LPF_B, LOW); digitalWrite(LPF_C, LOW); digitalWrite(LPF_D, LOW); - //printf("################ setting %d high\n", lpf); +#if DEBUG > 0 + printf("################ setting %d high\n", lpf); +#endif digitalWrite(lpf, HIGH); prev_lpf = lpf; } @@ -362,8 +387,15 @@ struct rx *add_tx(int frequency, short mode, int bpf_low, int bpf_high){ //create fft complex arrays to convert the frequency back to time r->fft_time = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS); r->fft_freq = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS); - r->plan_rev = fftw_plan_dft_1d(MAX_BINS, r->fft_freq, r->fft_time, FFTW_BACKWARD, FFTW_ESTIMATE); - + + int e = fftw_import_wisdom_from_filename(wisdom_file); + if (e == 0) + { + printf("Generating Wisdom File...\n"); + } + r->plan_rev = fftw_plan_dft_1d(MAX_BINS, r->fft_freq, r->fft_time, FFTW_BACKWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + fftw_export_wisdom_to_filename(wisdom_file); + r->output = 0; r->next = NULL; r->mode = mode; @@ -403,8 +435,15 @@ struct rx *add_rx(int frequency, short mode, int bpf_low, int bpf_high){ //create fft complex arrays to convert the frequency back to time r->fft_time = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS); r->fft_freq = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * MAX_BINS); - r->plan_rev = fftw_plan_dft_1d(MAX_BINS, r->fft_freq, r->fft_time, FFTW_BACKWARD, FFTW_ESTIMATE); - + + int e = fftw_import_wisdom_from_filename(wisdom_file); + if (e == 0) + { + printf("Generating Wisdom File...\n"); + } + r->plan_rev = fftw_plan_dft_1d(MAX_BINS, r->fft_freq, r->fft_time, FFTW_BACKWARD, WISDOM_MODE); // Was FFTW_ESTIMATE N3SB + fftw_export_wisdom_to_filename(wisdom_file); + r->output = 0; r->next = NULL; r->mode = mode; @@ -511,8 +550,6 @@ void rx_process(int32_t *input_rx, int32_t *input_mic, int i, j = 0; double i_sample, q_sample; - - //STEP 1: first add the previous M samples to for (i = 0; i < MAX_BINS/2; i++) fft_in[i] = fft_m[i]; @@ -813,7 +850,6 @@ void sound_process( int32_t *output_speaker, int32_t *output_tx, int n_samples) { - if (in_tx) tx_process(input_rx, input_mic, output_speaker, output_tx, n_samples); else @@ -1071,7 +1107,7 @@ void tr_switch_de(int tx_on){ if (tr_relay){ digitalWrite(LPF_A, LOW); digitalWrite(LPF_B, LOW); - digitalWrite(LPF_C, LOW); + digitalWrite(LPF_C, LOW); digitalWrite(LPF_D, LOW); } delay(10); @@ -1094,7 +1130,7 @@ void tr_switch_v2(int tx_on){ //first turn off the LPFs, so PA doesnt connect digitalWrite(LPF_A, LOW); digitalWrite(LPF_B, LOW); - digitalWrite(LPF_C, LOW); + digitalWrite(LPF_C, LOW); digitalWrite(LPF_D, LOW); //mute it all and hang on for a millisecond @@ -1127,7 +1163,7 @@ void tr_switch_v2(int tx_on){ digitalWrite(LPF_A, LOW); digitalWrite(LPF_B, LOW); - digitalWrite(LPF_C, LOW); + digitalWrite(LPF_C, LOW); digitalWrite(LPF_D, LOW); prev_lpf = -1; //force the lpf to be re-energized delay(10); @@ -1383,5 +1419,3 @@ void sdr_request(char *request, char *response){ /* else printf("*Error request[%s] not accepted\n", request); */ } - - From ab3e7369abd9ad62b2497b78f74946d980897723 Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sun, 31 Dec 2023 16:38:16 -0500 Subject: [PATCH 5/7] Return build to original configuration --- build | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build b/build index 29cc0ac3..5eb20089 100755 --- a/build +++ b/build @@ -1,10 +1,5 @@ #!/bin/sh F=$@ -if [ "$F" = "" ]; then - echo "No build target provided" - echo "Assuming target is sbitx" - F=sbitx -fi WORKING_DIRECTORY=`pwd` echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" date From 3372f81adbdfe4406e71a2b6940352d655af965b Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sun, 31 Dec 2023 16:41:51 -0500 Subject: [PATCH 6/7] Return sbitx_sound.c to original configuration --- sbitx_sound.c | 384 ++++++++++---------------------------------------- 1 file changed, 74 insertions(+), 310 deletions(-) diff --git a/sbitx_sound.c b/sbitx_sound.c index a7ec32a0..ebaea28d 100644 --- a/sbitx_sound.c +++ b/sbitx_sound.c @@ -3,18 +3,11 @@ #include #include #include -#include #include #include "sound.h" #include "wiringPi.h" #include "sdr.h" -// Set the DEBUG define to 1 to compile in the debugging messages. -// Set the DEBUG define to 2 to compile in detailed error reporting debugging messages. -#define DEBUG 0 - -// Set the DISABLE_LOOPBACK define to 1 to disable the loopback processing. -#define DISABLE_LOOPBACK 0 /* follows the tutorial at http://alsamodular.sourceforge.net/alsa_programming_howto.html Next thing to try is http://www.saunalahti.fi/~s7l/blog/2005/08/21/Full%20Duplex%20ALSA @@ -30,13 +23,6 @@ Next thing to try is http://www.saunalahti.fi/~s7l/blog/2005/08/21/Full%20Duplex configuring the Raspberry Pi with Wolfson codec. */ -/* - The audio channels are: - PCM Playback - Loopback Capture - PCM Capture - Loopback Play -*/ /* MIXER api @@ -142,7 +128,7 @@ void sound_mixer(char *card_name, char *element, int make_on) } int rate = 96000; /* Sample rate */ -static snd_pcm_uframes_t buff_size = 8192; /* Periodsize (bytes) */ +static snd_pcm_uframes_t buff_size = 8192; /* Periodsize (bytes) */ static int n_periods_per_buffer = 2; /* Number of periods */ //static int n_periods_per_buffer = 1024; /* Number of periods */ @@ -164,10 +150,8 @@ static int sound_thread_continue = 0; pthread_t sound_thread, loopback_thread; #define LOOPBACK_LEVEL_DIVISOR 8 // Constant used to reduce audio level to the loopback channel (FLDIGI) -static int pcm_capture_error = 0; // count pcm capture errors -static int pcm_play_write_error = 0; // count play channel write errors -static int pcm_loopback_write_error = 0; // count loopback channel write errors -static int result = 0; // scratch variable for storing function call results +static int play_write_error = 0; // count play channel write errors +static int loopback_write_error = 0; // count loopback channel write errors // Note: Error messages appear when the sbitx program is started from the command line int use_virtual_cable = 0; @@ -185,29 +169,23 @@ The sequence of the alsa functions must be maintained for this to work consisten It returns a -1 if the device didn't open. The error message is on stderr. IMPORTANT: -The sound is playback is carried on in a non-blocking way -Update - Sound playback now uses blocking ALSA calls, although the calls -do not block until the buffers are completely filled (which will never occur) +The sound is playback is carried on in a non-blocking way */ int sound_start_play(char *device){ //found out the correct device through aplay -L (for pcm devices) + + snd_pcm_hw_params_alloca(&hwparams); //more alloc + //puts a playback handle into the pointer to the pointer - -#if DEBUG > 0 - printf ("opening audio playback stream to %s\n", device); -#endif - int e = snd_pcm_open(&pcm_play_handle, device, play_stream, 0); // was SND_PCM_NONBLOCK + int e = snd_pcm_open(&pcm_play_handle, device, play_stream, SND_PCM_NONBLOCK); if (e < 0) { fprintf(stderr, "Error opening PCM playback device %s: %s\n", device, snd_strerror(e)); return -1; } - snd_pcm_hw_params_alloca(&hwparams); // more alloc - snd_pcm_sw_params_alloca(&swparams); // more alloc - //fills up the hwparams with values, hwparams was allocated above e = snd_pcm_hw_params_any(pcm_play_handle, hwparams); @@ -215,7 +193,7 @@ int sound_start_play(char *device){ fprintf(stderr, "*Error getting hw playback params (%d)\n", e); return(-1); } - + // set the pcm access to interleaved e = snd_pcm_hw_params_set_access(pcm_play_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { @@ -241,10 +219,9 @@ int sound_start_play(char *device){ } if (rate != exact_rate) fprintf(stderr, "*The playback rate %d changed to %d Hz\n", rate, exact_rate); -#if DEBUG > 0 - else +/* else fprintf(stderr, "Playback sampling rate is set to %d\n", exact_rate); -#endif +*/ /* Set number of channels */ if ((e = snd_pcm_hw_params_set_channels(pcm_play_handle, hwparams, 2)) < 0) { @@ -252,8 +229,7 @@ int sound_start_play(char *device){ return(-1); } -/* -// This function call and the next have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 + // frame = bytes_per_sample x n_channel // period = frames transfered at a time (160 for voip, etc.) // we use two periods per buffer. @@ -261,50 +237,22 @@ int sound_start_play(char *device){ fprintf(stderr, "*Error setting playback periods.\n"); return(-1); } -*/ + // the buffer size is each periodsize x n_periods - // snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8*4; // A Larger buffer - N3SB Hack -#if DEBUG > 0 - printf("trying for buffer size of %ld\n", n_frames); -#endif -/* -// This function call and the previous have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; + //printf("trying for buffer size of %ld\n", n_frames); e = snd_pcm_hw_params_set_buffer_size_near(pcm_play_handle, hwparams, &n_frames); if (e < 0) { fprintf(stderr, "*Error setting playback buffersize.\n"); return(-1); } -*/ - // This function call replaces the two function calls above - N3SB December 2023 - e = snd_pcm_hw_params_set_period_size_near(pcm_play_handle, hwparams, &n_frames, 0); - if (e < 0) { - fprintf(stderr, "*Error setting playback buffersize.\n"); - return(-1); - } if (snd_pcm_hw_params(pcm_play_handle, hwparams) < 0) { fprintf(stderr, "*Error setting playback HW params.\n"); return(-1); } - - // get the current swparams - e = snd_pcm_sw_params_current(pcm_play_handle, swparams); - if (e < 0) { - printf("Unable to determine current swparams for playback: %s\n", snd_strerror(e)); - } - - e = snd_pcm_sw_params_set_start_threshold(pcm_play_handle, swparams, (8192) ); - if (e < 0) { - printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(e)); - } - - -#if DEBUG > 0 - printf("PCM Playback Buffer Size: %d\n",snd_pcm_avail(pcm_play_handle)); - puts("All hw params set to play sound"); -#endif +// puts("All hw params set to play sound"); return 0; } @@ -313,10 +261,7 @@ int sound_start_play(char *device){ int sound_start_loopback_capture(char *device){ snd_pcm_hw_params_alloca(&hloop_params); - -#if DEBUG > 0 - printf ("opening audio loopback tx stream to %s\n", device); -#endif + //printf ("opening audio tx stream to %s\n", device); int e = snd_pcm_open(&loopback_capture_handle, device, capture_stream, 0); if (e < 0) { @@ -363,31 +308,22 @@ int sound_start_loopback_capture(char *device){ return(-1); } -/* //printf("%d: set the #channels\n", __LINE__, 2); - // Set number of periods. Periods used to be called fragments. + /* Set number of periods. Periods used to be called fragments. */ if ((e = snd_pcm_hw_params_set_periods(loopback_capture_handle, hloop_params, n_periods_per_buffer, 0)) < 0) { fprintf(stderr, "*Error setting loopback capture periods.\n"); return(-1); } // the buffer size is each periodsize x n_periods - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer) / 8; - // printf("trying for buffer size of %ld\n", n_frames); + snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; + //printf("trying for buffer size of %ld\n", n_frames); e = snd_pcm_hw_params_set_buffer_size_near(loopback_capture_handle, hloop_params, &n_frames); if (e < 0) { fprintf(stderr, "*Error setting loopback capture buffersize.\n"); return(-1); } -*/ - // This function call and the previous have been replaced by the snd_pcm_hw_params_set_period_size_near() function call - N3SB December 2023 - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer) / 8; - e = snd_pcm_hw_params_set_period_size_near(loopback_capture_handle, hloop_params, &n_frames, 0); - if (e < 0) { - fprintf(stderr, "*Error setting loopback capture buffersize.\n"); - return(-1); - } - + //printf("%d: set buffer to \n", __LINE__, n_frames); if (snd_pcm_hw_params(loopback_capture_handle, hloop_params) < 0) { fprintf(stderr, "*Error setting capture HW params.\n"); @@ -407,14 +343,9 @@ int sound_start_loopback_capture(char *device){ } if ((e = snd_pcm_sw_params_set_stop_threshold(loopback_capture_handle, sloop_params, 1)) < 0){ + fprintf(stderr, "Unable to set stop threshold for loopback capture\n"); } - -#if DEBUG > 0 - printf("Loopback Capture Buffer Size: %d\n",snd_pcm_avail(loopback_capture_handle)); - puts("All hw params set for loopback capture sound"); -#endif - return 0; } @@ -428,10 +359,7 @@ just wait for the next block to arrive int sound_start_capture(char *device){ snd_pcm_hw_params_alloca(&hwparams); - -#if DEBUG > 0 - printf ("opening PCM Capture stream to %s\n", device); -#endif + int e = snd_pcm_open(&pcm_capture_handle, device, capture_stream, 0); if (e < 0) { @@ -442,77 +370,65 @@ int sound_start_capture(char *device){ e = snd_pcm_hw_params_any(pcm_capture_handle, hwparams); if (e < 0) { - fprintf(stderr, "*Error setting PCM capture access (%d)\n", e); + fprintf(stderr, "*Error setting capture access (%d)\n", e); return(-1); } e = snd_pcm_hw_params_set_access(pcm_capture_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { - fprintf(stderr, "*Error setting PCM capture access.\n"); + fprintf(stderr, "*Error setting capture access.\n"); return(-1); } /* Set sample format */ e = snd_pcm_hw_params_set_format(pcm_capture_handle, hwparams, SND_PCM_FORMAT_S32_LE); if (e < 0) { - fprintf(stderr, "*Error setting PCM capture format.\n"); + fprintf(stderr, "*Error setting capture format.\n"); return(-1); } + /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_rate = rate; e = snd_pcm_hw_params_set_rate_near(pcm_capture_handle, hwparams, &exact_rate, 0); if ( e< 0) { - fprintf(stderr, "*Error setting PCM capture rate.\n"); + fprintf(stderr, "*Error setting capture rate.\n"); return(-1); } if (rate != exact_rate) - fprintf(stderr, "#The PCM capture rate %d changed to %d Hz\n", rate, exact_rate); + fprintf(stderr, "#The capture rate %d changed to %d Hz\n", rate, exact_rate); /* Set number of channels */ if ((e = snd_pcm_hw_params_set_channels(pcm_capture_handle, hwparams, 2)) < 0) { - fprintf(stderr, "*Error setting PCM capture channels.\n"); + fprintf(stderr, "*Error setting capture channels.\n"); return(-1); } -/* - // Set number of periods. Periods used to be called fragments. + + /* Set number of periods. Periods used to be called fragments. */ if ((e = snd_pcm_hw_params_set_periods(pcm_capture_handle, hwparams, n_periods_per_buffer, 0)) < 0) { fprintf(stderr, "*Error setting capture periods.\n"); return(-1); } + // the buffer size is each periodsize x n_periods snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; //printf("trying for buffer size of %ld\n", n_frames); - e = snd_pcm_hw_params_set_buffer_size_near(pcm_capture_handle, hwparams, &n_frames); - if (e < 0) { - fprintf(stderr, "*Error setting PCM capture buffersize.\n"); - return(-1); - } -*/ - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/ 8; - // This function call replaces the two function calls above - N3SB December 2023 - e = snd_pcm_hw_params_set_period_size_near(pcm_capture_handle, hwparams, &n_frames, 0); + e = snd_pcm_hw_params_set_buffer_size_near(pcm_play_handle, hwparams, &n_frames); if (e < 0) { - fprintf(stderr, "*Error setting PCM Capture buffersize.\n"); + fprintf(stderr, "*Error setting capture buffersize.\n"); return(-1); } - if (snd_pcm_hw_params(pcm_capture_handle, hwparams) < 0) { - fprintf(stderr, "*Error setting PCM capture HW params.\n"); + fprintf(stderr, "*Error setting capture HW params.\n"); return(-1); } -#if DEBUG > 0 - printf("Capture Buffer Size: %d\n",snd_pcm_avail(pcm_capture_handle)); - puts("All hw params set for PCM sound capture"); -#endif - return 0; } @@ -521,10 +437,8 @@ int sound_start_loopback_play(char *device){ snd_pcm_hw_params_alloca(&hwparams); //more alloc -#if DEBUG > 0 - printf ("opening Loopback Play stream to %s\n", device); -#endif - int e = snd_pcm_open(&loopback_play_handle, device, play_stream, 0); // was SND_PCM_NONBLOCK + //printf ("opening audio rx stream to %s\n", device); + int e = snd_pcm_open(&loopback_play_handle, device, play_stream, SND_PCM_NONBLOCK); if (e < 0) { fprintf(stderr, "Error opening loopback playback device %s: %s\n", device, snd_strerror(e)); @@ -540,14 +454,14 @@ int sound_start_loopback_play(char *device){ e = snd_pcm_hw_params_set_access(loopback_play_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (e < 0) { - fprintf(stderr, "*Error setting loopback Play access.\n"); + fprintf(stderr, "*Error setting loopback access.\n"); return(-1); } /* Set sample format */ e = snd_pcm_hw_params_set_format(loopback_play_handle, hwparams, SND_PCM_FORMAT_S32_LE); if (e < 0) { - fprintf(stderr, "*Error setting loopback Play format.\n"); + fprintf(stderr, "*Error setting loopback format.\n"); return(-1); } @@ -569,7 +483,7 @@ int sound_start_loopback_play(char *device){ return(-1); } -/* + // frame = bytes_per_sample x n_channel // period = frames transfered at a time (160 for voip, etc.) // we use two periods per buffer. @@ -578,6 +492,7 @@ int sound_start_loopback_play(char *device){ return(-1); } + // the buffer size is each periodsize x n_periods snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; //lets pump it up to see if we can reduce the dropped frames @@ -588,27 +503,14 @@ int sound_start_loopback_play(char *device){ fprintf(stderr, "*Error setting loopback playback buffersize.\n"); return(-1); } -*/ - // This function call replaces the two function calls above - N3SB December 2023 - snd_pcm_uframes_t n_frames= (buff_size * n_periods_per_buffer)/8; - //lets pump it up to see if we can reduce the dropped frames - n_frames *= 4; - e = snd_pcm_hw_params_set_period_size_near(loopback_play_handle, hwparams, &n_frames, 0); - if (e < 0) { - fprintf(stderr, "*Error setting loopback play buffersize.\n"); - return(-1); - } + + //printf("loopback playback buffer size is set to %d\n", n_frames); if (snd_pcm_hw_params(loopback_play_handle, hwparams) < 0) { fprintf(stderr, "*Error setting loopback playback HW params.\n"); return(-1); } -#if DEBUG > 0 - printf("Loopback Playback Buffer Size: %d\n",snd_pcm_avail(pcm_capture_handle)); - puts("All hw params set for Loopback Playback"); -#endif - return 0; } @@ -641,20 +543,6 @@ static int nframes = 0; int32_t resample_in[10000]; int32_t resample_out[10000]; -struct timeval GetTimeStamp() -{ - struct timeval tv; - gettimeofday(&tv,NULL); - return tv; -} -struct timeval tv; -signed long pcm_read_old_time = 0; -signed long pcm_read_new_time = 0; -signed long delta_time = 0; -unsigned long samples_read = 0; -unsigned long samples_written = 0; -static unsigned long loop_counter = 0; - int last_second = 0; int nsamples = 0; int played_samples = 0; @@ -664,8 +552,6 @@ int sound_loop(){ int pcmreturn, i, j, loopreturn; short s1, s2; int frames; - int pcm_read_avail; - int pcm_write_avail; //we allocate enough for two channels of int32_t sized samples data_in = (int32_t *)malloc(buff_size * 2); @@ -678,26 +564,14 @@ int sound_loop(){ output_q = (int32_t *)malloc(buff_size * 2); frames = buff_size / 8; - - + snd_pcm_prepare(pcm_play_handle); snd_pcm_prepare(loopback_play_handle); + snd_pcm_writei(pcm_play_handle, data_out, frames); + snd_pcm_writei(pcm_play_handle, data_out, frames); -/* - pcmreturn = snd_pcm_writei(pcm_play_handle, data_out, frames*2); // Get a head start on filling the queue -#if DEBUG > 0 - printf("Pre-filling play and loopback queues\n"); - printf("Playback buffer filled with %d samples\n",pcmreturn); -#endif - pcmreturn = snd_pcm_writei(loopback_play_handle, data_out, frames); // Get a head start on filling the queue -#if DEBUG > 0 - printf("Loopback buffer filled with %d samples\n",pcmreturn); -#endif -*/ //Note: the virtual cable samples queue should be flushed at the start of tx qloop.stall = 1; - -// ******************************************************************************************************** The Big Loop starts here while(sound_thread_continue) { @@ -705,51 +579,21 @@ int sound_loop(){ //this is opened as a blocking device, hence we derive accurate timing last_time = gettime_now.tv_nsec/1000; - - // printf("%d\n", last_time); - pcm_read_avail = snd_pcm_avail(pcm_capture_handle); - - tv = GetTimeStamp(); // get time - pcm_read_new_time= 1000000 * tv.tv_sec + tv.tv_usec; // Store time in microseconds - delta_time = pcm_read_new_time - pcm_read_old_time; - if ((delta_time > 11667) || (delta_time < 9667)) // Loop should iterate every 10667 microseconds - { -#if DEBUG > 1 - printf("Loop Counter: %d, Loop Time: %d, Samples available: %d\n", loop_counter, delta_time, pcm_read_avail); -#endif - } - pcm_read_old_time = pcm_read_new_time; - - while ((pcmreturn = snd_pcm_readi(pcm_capture_handle, data_in, frames)) < 0) - { - result = snd_pcm_prepare(pcm_capture_handle); -#if DEBUG > 0 - printf("**** PCM Capture Error: %s count = %d\n",snd_strerror(pcmreturn), pcm_capture_error++); -#endif + + while ((pcmreturn = snd_pcm_readi(pcm_capture_handle, data_in, frames)) < 0){ + snd_pcm_prepare(pcm_capture_handle); + //putchar('='); } -#if DEBUG > 1 - printf("Delta Time: %d, Available output sample storage: %d\n", delta_time, snd_pcm_avail(pcm_play_handle)); -#endif - samples_read += pcmreturn; - i = 0; j = 0; -#if DEBUG > 0 - if (pcmreturn < 1024) - printf("\n----PCM Read Size = %d\n",pcmreturn); -#endif int ret_card = pcmreturn; - if (use_virtual_cable) - { + if (use_virtual_cable){ //printf(" we have %d in qloop, writing now\n", q_length(&qloop)); // if don't we have enough to last two iterations loop back... - if (q_length(&qloop) < pcmreturn) - { -#if DEBUG > -1 - puts(" skipping\n"); -#endif + if (q_length(&qloop) < pcmreturn){ +// puts(" skipping"); continue; } @@ -764,7 +608,7 @@ int sound_loop(){ j++; } played_samples += 1024; - } // end for use_virtual_cable test + } else { while (i < ret_card){ input_i[i] = data_in[j++]/2; @@ -773,7 +617,7 @@ int sound_loop(){ } } - // printf("\n-%d %ld %d\n", count++, nsamples, pcmreturn); + //printf("%d %ld %d\n", count++, nsamples, pcmreturn); sound_process(input_i, input_q, output_i, output_q, ret_card); @@ -784,55 +628,19 @@ int sound_loop(){ data_out[j++] = output_q[i++]; } + int framesize = ret_card; int offset = 0; int play_write_errors = 0; - int pswitch = 0; while(framesize > 0) { - do - { - pcmreturn = snd_pcm_avail(pcm_play_handle); - } while ((pcmreturn == 0) || (pcmreturn == -11)); - - do - { - pcmreturn = snd_pcm_writei(pcm_play_handle, data_out + offset, framesize); - } while (pcmreturn == -11); - - if ((pcmreturn > 0) && (pcmreturn < 1024)) - { -#if DEBUG > 0 - printf("#### Partial Write ####"); // would signify that the playback channel didn't accept all the samples -#endif - } - // if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors - if(pcmreturn < 0) + pcmreturn = snd_pcm_writei(pcm_play_handle, data_out + offset, framesize); + if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors { // Handle an error condition from the snd_pcm_writei function -#if DEBUG > 0 - printf("Loop Counter: %d, Play PCM Write Error %d: %s count = %d\n",loop_counter, pcmreturn, snd_strerror(pcmreturn), pcm_play_write_error++); -#endif - if (pcmreturn == -EPIPE) - { -#if DEBUG > 0 - printf("Samples Read: %d, Samples Written: %d, delta: %d, available %d\n", samples_read, samples_written, samples_read - samples_written, pcm_write_avail); - printf("Available write buffer: %d\n", pcm_write_avail); -#endif - snd_pcm_recover(pcm_play_handle, pcmreturn, 0); - } - - -#if DEBUG > 0 - printf("Now Available write buffer: %d\n", snd_pcm_avail(pcm_play_handle)); -#endif - -#if DEBUG <2 - // snd_pcm_recover(pcm_play_handle, pcmreturn, 1); // Does not provide detailed error message -#else - // snd_pcm_recover(pcm_play_handle, pcmreturn, 0); // Provides detailed error message -#endif +// printf("Play PCM Write Error: %s count = %d\n",snd_strerror(pcmreturn), play_write_error++); + snd_pcm_prepare(pcm_play_handle); } if(pcmreturn >= 0) @@ -840,37 +648,12 @@ int sound_loop(){ // Calculate remaining number of samples to be sent and new position in sample array. // If all the samples were processed by the snd_pcm_writei function then framesize will be // zero and the while() loop will end. - if (pswitch == 1) - { -#if DEBUG > 0 - printf("%d ",pcmreturn); -#endif - } framesize -= pcmreturn; - if ((framesize > 0) && (pswitch == 0)) - { -#if DEBUG > 0 - printf("%d ",pcmreturn); -#endif - pswitch = 1; - } offset += (pcmreturn * 2); - samples_written += pcmreturn; - } - if (framesize == 0) - { - if (pswitch == 1) - { -#if DEBUG > 0 - printf("\n"); -#endif - pswitch = 0; - } } } // End of new pcm play write routine -#if DISABLE_LOOPBACK == 0 //decimate the line out to half, ie from 96000 to 48000 //play the received data (from left channel) to both of line out @@ -892,30 +675,15 @@ int sound_loop(){ while(framesize > 0) { - do - { - pcmreturn = snd_pcm_avail(loopback_play_handle); - } while ((pcmreturn == 0) || (pcmreturn == -11)); - // printf("Writing %d frame to loopback\n", framesize); - - do - { pcmreturn = snd_pcm_writei(loopback_play_handle, line_out + offset, framesize); - } while (pcmreturn == -11); - - // if((pcmreturn < 0) && (pcmreturn != -11)) // also ignore "temporarily unavailable" errors if(pcmreturn < 0) - { // Handle an error condition from the snd_pcm_writei function -#if DEBUG > 0 - printf("Loopback PCM Write %d bytes Error %d: %s count = %d\n", framesize, pcmreturn, snd_strerror(pcmreturn), pcm_loopback_write_error++); -#endif - -#if DEBUG <2 - snd_pcm_recover(loopback_play_handle, pcmreturn, 1); // Does not provide detailed error message -#else - snd_pcm_recover(loopback_play_handle, pcmreturn, 0); // Provides detailed error message -#endif + { + //printf("Loopback PCM Write %d bytes Error: %s count = %d\n", + // framesize, snd_strerror(pcmreturn), loopback_write_error++); + // Handle an error condition from the snd_pcm_writei function + play_write_errors++; + snd_pcm_prepare(loopback_play_handle); } if(pcmreturn >= 0) @@ -925,21 +693,17 @@ int sound_loop(){ // zero and the while() loop will end. framesize -= pcmreturn; offset += (pcmreturn * 2); - if (framesize > 0) - { -#if DEBUG > 0 - printf("\nLoopback - pcmreturn = %d\n", pcmreturn); -#endif - } } } // End of new pcm loopback write routine - -#endif + + if(play_write_errors > 20){ +// printf("play write error!\n"); + play_write_errors = 0; + } //played_samples += pcmreturn; - loop_counter++; - } // End of while (sound_thread_continue) loop + } //fclose(pf); printf("********Ending sound thread\n"); } @@ -959,7 +723,7 @@ int loopback_loop(){ while(sound_thread_continue) { - //restart the loopback capture if there is an error reading the samples + //restart the pcm capture if there is an error reading the samples //this is opened as a blocking device, hence we derive accurate timing last_time = gettime_now.tv_nsec/1000; From 15a52df546031c6e323525480e9e898fc0f00f41 Mon Sep 17 00:00:00 2001 From: Steve Beckman Date: Sat, 27 Jan 2024 17:54:45 -0500 Subject: [PATCH 7/7] Update sbitx_gtk.c to correct which VFO the receiver is set to Further details about the problem and how to duplicate it are in issue report # 87. In SPLIT VFO mode, the frequency in VFO B was being set as the receive frequency. The receiver should be set to the VFO A frequency. --- sbitx_gtk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbitx_gtk.c b/sbitx_gtk.c index ce66249c..7d973998 100644 --- a/sbitx_gtk.c +++ b/sbitx_gtk.c @@ -2327,7 +2327,7 @@ void set_operating_freq(int dial_freq, char *response){ } else if (!strcmp(split->value, "ON")){ if (!in_tx) - sprintf(freq_request, "r1:freq=%s", vfo_b->value); + sprintf(freq_request, "r1:freq=%s", vfo_a->value); else sprintf(freq_request, "r1:freq=%d", dial_freq); }