From 40b886acc7d745c7e6bc49899d43053040aeb366 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Fri, 6 Sep 2024 13:42:42 -0700 Subject: [PATCH 01/18] Add RX Audio Statistics and ADC Clip Detection feature. This adds a new rxaudiostats parameter to simpleusb.conf, which enables a new function check_rx_audio() in chan_simpleusb.c to be called during processing of received USB audio frames. If ADC clipping is then detected GPIO4 is set for 500mS to support illumination of a node / audio interface Clip LED. Statistics collected also include peak and average RMS audio levels averaged over the previous 1 second, which can be displayed from the simpleusb-tune-menu 'R' option or AMI 'susb tune menu-support a' function. --- channels/chan_simpleusb.c | 200 ++++++++++++++++++++++++++ configs/rpt/simpleusb.conf | 4 + configs/samples/simpleusb.conf.sample | 4 + utils/simpleusb-tune-menu.c | 6 + 4 files changed, 214 insertions(+) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index ffd586f..7a32e72 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -272,6 +272,7 @@ struct chan_simpleusb_pvt { int hid_io_ctcss; int hid_io_ctcss_loc; int hid_io_ptt; + int hid_io_clip_led; /* indicator to alert user of ADC clipping */ int hid_gpio_loc; int32_t hid_gpio_val; int32_t valid_gpios; @@ -314,6 +315,7 @@ struct chan_simpleusb_pvt { struct timeval tonetime; int toneflag; int duplex3; + int rxaudiostats; int32_t discfactor; int32_t discounterl; @@ -325,6 +327,15 @@ struct chan_simpleusb_pvt { int32_t cur_gpios; char *gpios[GPIO_PINCOUNT]; char *pps[32]; + + /* Rx audio (ADC) statistics variables. susb tune-menu "R" command displays + * stats data (peak, average, min, max levels and clipped sample count). + */ +#define AUDIO_STATS_LEN 50 /* number of 20mS frames. 50 => 1 second buf len */ + unsigned short maxbuf[AUDIO_STATS_LEN]; /* peak sample value per frame */ + unsigned short clipbuf[AUDIO_STATS_LEN]; /* number of clipped samples per frame */ + unsigned int pwrbuf[AUDIO_STATS_LEN]; /* total RMS power per frame */ + short rxaudiostats_index; /* Index within buffers, updated as frames received */ ast_mutex_t usblock; }; @@ -344,6 +355,8 @@ static struct chan_simpleusb_pvt simpleusb_default = { .rxondelay = 0, .txoffdelay = 0, .pager = PAGER_NONE, + .rxaudiostats = 1, + .rxaudiostats_index = 0 }; /* DECLARE FUNCTION PROTOTYPES */ @@ -367,6 +380,8 @@ static int simpleusb_setoption(struct ast_channel *chan, int option, void *data, static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *cmd); static void tune_write(struct chan_simpleusb_pvt *o); static int _send_tx_test_tone(int fd, struct chan_simpleusb_pvt *o, int ms, int intflag); +static void check_rx_audio(struct chan_simpleusb_pvt *o, short len); +static void print_rx_audio_stats(int fd, struct chan_simpleusb_pvt *o); static char *simpleusb_active; /* the active device */ @@ -559,6 +574,7 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 8; /* GPIO 4 is PTT */ + o->hid_io_clip_led = 0; /* No Clip LED on this HW */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } else if (o->hdwtype == 0) { //dudeusb @@ -569,6 +585,7 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 1; /* VOL UP External CTCSS */ o->hid_io_ctcss_loc = 0; /* VOL UP External CTCSS */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ + o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 0xfb; /* for GPIO 1,2,4,5,6,7,8 (5,6,7,8 for CM-119 only) */ } else if (o->hdwtype == 2) { //NHRC (N1KDO) (dudeusb w/o user GPIO) @@ -579,6 +596,7 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 1; /* VOL UP is External CTCSS */ o->hid_io_ctcss_loc = 0; /* VOL UP CTCSS */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ + o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 0; /* for GPIO 1,2,4 */ } else if (o->hdwtype == 3) { // custom version @@ -589,6 +607,7 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ + o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } @@ -2267,6 +2286,16 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) } } + /* Check for ADC clipping and input audio statistics before any filtering is done. + * FRAME_SIZE define refers to 8Ksps mono which is 160 samples per 20mS USB frame. + * check_rx_audio() takes the read buffer as received (48K stereo), extracts the + * mono 48K channel and checks amplitude and distortion characteristics. + */ + if(o->rxaudiostats) + { + check_rx_audio(o, 12 * FRAME_SIZE); + } + /* Downsample received audio from 48000 stereo to 8000 mono */ sp = (short *) o->simpleusb_read_buf; sp1 = (short *) (o->simpleusb_read_frame_buf + AST_FRIENDLY_OFFSET); @@ -3256,6 +3285,7 @@ static void tune_write(struct chan_simpleusb_pvt *o) * 1 - get node names that are configured in simpleusb.conf * 2 - print parameters * 3 - get node names that are configured in simpleusb.conf, except current device + * a - receive audio statistics display * b - receiver tune display * c - receive level * f - txa level @@ -3317,6 +3347,24 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c } ast_cli(fd, "\n"); break; + case 'a': /* display receive audio statistics (interactive) */ + case 'A': /* display receive audio statistics (once only) */ + if (!o->hasusb) { + ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); + break; + } + if (!o->rxaudiostats) { + ast_cli(fd, "rxaudiostats is currently Disabled in simpleusb.conf\n"); + break; + } + for (;;) { + print_rx_audio_stats(fd, o); + if (cmd[0] == 'A') + break; + if (ast_radio_poll_input(fd, 1000)) + break; + } + break; case 'b': /* receiver tune display */ if (!o->hasusb) { ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); @@ -3650,6 +3698,7 @@ static struct chan_simpleusb_pvt *store_config(const struct ast_config *cfg, con CV_BOOL("deemphasis", o->deemphasis); CV_BOOL("preemphasis", o->preemphasis); CV_UINT("duplex3", o->duplex3); + CV_BOOL("rxaudiostats", o->rxaudiostats); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { @@ -4012,6 +4061,157 @@ static int unload_module(void) return 0; } +/*! + * \brief Detect ADC clipping, collect Rx audio statistics. + * + * If enabled by conf settings will set GPIO4 high for 500mS when clipping is + * detected. Nodes/URIs/audio interfaces can then light a Clip LED to alert users + * of excessive audio input levels. Because CM1xxx USB audio interface ICs have an + * internal mixer ahead of the ADC it is not possible within the interface board + * analog circuitry to detect clipping at the ADC input point, thus this function + * enables the raw ADC data to be checked. Clipping is detected by looking for + * large amplitude square waves (min. 3 samples in a row > 99% FS). + * + * Data collected can be displayed from the simpleusb-tune-menu 'R' option or AMI + * "susb tune menu-support a" function. This also shows average power levels which + * can be of further use in optimizing audio levels, compression, limiting, etc. + * In general, peak levels should be within 6-10dB of full-scale (0dBFS) and + * average signal power levels should be 6-12dB below peak levels. + * + * Should be passed the raw 48Ksps stereo USB frame read buffer before any + * filtering or downsampling has been done. Extracts the 48K mono channel and + * downsamples to 8Ksps (as is done in simpleusb_read() but without filtering). + * Signal power calculation takes the square of each sample to measure RMS power. + * For CPU efficiency no scaling is done here. (When stats data is printed the + * values are scaled to dBFS.) + * + * Audio parameters of interest include: + * - Peak signal level over a longer time period eg. 1+ seconds (dBFS) + * This defines headroom (dB) and potential for clipping + * - Min and max signal power levels averaged within each USB frame (dBFS) + * These define average dynamic range (dB) + * - Min and max signal power averaged over a longer time period (dBFS) + * These define total signal power and peak-to-average power ratio + * + * \author NR9V + * \param o Channel data structure + * \param len Length of data within o->simpleusb_read_buf + * \return None + */ +#define CLIP_SAMP_THRESH 0x7eb0 +#define CLIP_EVENT_MIN_SAMPLES 3 +#define CLIP_LED_HOLD_TIME_MS 500 +static void check_rx_audio(struct chan_simpleusb_pvt *o, short len) +{ + short *sbuf = (short *) o->simpleusb_read_buf; + unsigned short i, j, val, max=0, clip_cnt=0, seq_clips=0, last_clip=-1; + double pwr=0.0; + short buf[FRAME_SIZE]; + + if(len > 12 * FRAME_SIZE) + len = 12 * FRAME_SIZE; + if(o->rxaudiostats_index >= AUDIO_STATS_LEN) + o->rxaudiostats_index = 0; + /* Downsample from 48000 stereo to 8000 mono */ + for(i=10, j=0; i < len; i += 12) + { + buf[j++] = sbuf[i]; + } + len /= 12; + /* len should now be 160 */ + for(i=0; i < len; i++) + { + val = abs(buf[i]); + if(val) + { + if(val > max) + max = val; + pwr += (double) (val * val); + if(val > CLIP_SAMP_THRESH) + { + clip_cnt++; + if(last_clip >= 0 && last_clip + 1 == i) + seq_clips++; + last_clip = i; + } + } + } + o->maxbuf[o->rxaudiostats_index] = max; + o->pwrbuf[o->rxaudiostats_index] = (unsigned int) (pwr / (double)len); + o->clipbuf[o->rxaudiostats_index] = seq_clips; + /* Set Clip LED if clipping detected and LED not already set */ + if(seq_clips >= CLIP_EVENT_MIN_SAMPLES && o->hid_io_clip_led && + !o->hid_gpio_pulsetimer[o->hid_io_clip_led]) + { + o->hid_gpio_pulsetimer[o->hid_io_clip_led] = CLIP_LED_HOLD_TIME_MS; + } + if(++o->rxaudiostats_index >= AUDIO_STATS_LEN) + { + o->rxaudiostats_index = 0; + } +} + +/*! + * \brief Display receive audio statistics. + * + * Display the audio stats buffer data in normalized units. Peak value is the largest + * sample value seen in the past AUDIO_STATS_LEN audio frames (1 second default). + * Average, min, and max signal power levels are calculated from the total signal + * power buffer which contains total RMS power per 20mS frame. Avg Pwr is the average + * of the power values in the buffer, min and max are the lowest and highest average + * power levels within the buffer. ClipCnt is the count of audio clipping events + * detected. + * + * Example output message: + * RxAudioStats: Pk -2.1 Avg Pwr -32 Min -60 Max -12 dBFS ClipCnt 0 + * + * Results are scaled to double precision 0.0-1.0 and converted to log (dB) + * ie. 10*log10(scaledVal) for power levels. + * + * \author NR9V + * \param fd File descriptor to print to, or if 0 print using ast_verbose() + * \param o Channel data structure + * \return None + */ +static void print_rx_audio_stats(int fd, struct chan_simpleusb_pvt *o) +{ + unsigned int pk=0, pwr=0, i, minpwr=0x40000000, maxpwr=0, clipcnt=0; + double tpwr=0.0, dpk, dmin, dmax, scale; + char s1[100]; + + /* Peak = max(maxbuf)^2 + * Avg Pwr = avg(pwrbuf) + * Min = min(pwrbuf) + * Max = max(pwrbuf) + */ + for(i=0; i < AUDIO_STATS_LEN; i++) + { + if(o->maxbuf[i] > pk) + pk = o->maxbuf[i]; + pwr = o->pwrbuf[i]; + if(pwr < minpwr) + minpwr = pwr; + if(pwr > maxpwr) + maxpwr = pwr; + tpwr += pwr; + clipcnt += o->clipbuf[i]; + } + tpwr /= AUDIO_STATS_LEN; + /* Convert to dBFS / dB */ + scale = 1.0 / (double) (1 << 30); + dpk = (pk > 0.0) ? 10 * log10(pk * pk * scale) : -96.0; + tpwr = (tpwr > 0.0) ? 10 * log10(tpwr * scale) : -96.0; + dmin = minpwr ? 10 * log10(minpwr * scale) : -96.0; + dmax = maxpwr ? 10 * log10(maxpwr * scale) : -96.0; + /* Print stats */ + sprintf(s1, "RxAudioStats: Pk %5.1f Avg Pwr %3.0f Min %3.0f Max %3.0f dBFS ClipCnt %u", + dpk, tpwr, dmin, dmax, clipcnt); + if(fd) + ast_cli(fd, "%s\n", s1); + else + ast_verbose("%s\n", s1); +} + AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SimpleUSB Radio Interface Channel Driver", .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index 7dbd2f5..8fdc583 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -108,6 +108,10 @@ preemphasis = no ; Perform standard 6db/octave pre-emphasis ; duplex3 = 0 ; duplex 3 gain setting (0 to disable) +rxaudiostats = yes ; Enable receive audio statistics and ADC clip detection (no to disable). + ; Enables tune-menu 'R' function. Supports audio interface Clip LED by + ; setting GPIO4 (if available) high for 500mS when clipping detected. + ;;; End of node-main template ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index 7243e84..a82e910 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -90,6 +90,10 @@ ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) +;rxaudiostats = yes ; Enable receive audio statistics and ADC clip detection (no to disable). + ; Enables tune-menu 'R' function. Supports audio interface Clip LED by + ; setting GPIO4 (if available) high for 500mS when clipping detected. + ;gpioX=in ; define input/output pin GPIO(x) in,out0,out1 (where X {1..8}) (optional) ;pport=/dev/parport0 ; Specify parport device (optional) diff --git a/utils/simpleusb-tune-menu.c b/utils/simpleusb-tune-menu.c index 8b70ab3..4749bc8 100644 --- a/utils/simpleusb-tune-menu.c +++ b/utils/simpleusb-tune-menu.c @@ -30,6 +30,7 @@ * 1 - get node names that are configured in simpleusb.conf * 2 - print parameters * 3 - get node names that are configured in simpleusb.conf, except current device + * a - receive audio statistics display * b - receiver tune display * c - receive level * f - txa level @@ -796,6 +797,7 @@ static int astgetresp(char *cmd) printf("K) Change RX On Delay (currently '%d')\n", rxondelay); printf("L) Change TX Off Delay (currently '%d')\n", txoffdelay); printf("P) Print Current Parameter Values\n"); + printf("R) View Rx Audio Statistics\n"); printf("S) Swap Current USB device with another USB device\n"); printf("T) Toggle Transmit Test Tone/Keying (currently '%s')\n", keying ? "enabled" : "disabled"); printf("V) View COS, CTCSS and PTT Status\n"); @@ -941,6 +943,10 @@ static int astgetresp(char *cmd) exit(255); } break; + case 'r': /* display receive audio statistics */ + case 'R': + astgetresp(COMMAND_PREFIX "tune menu-support a"); + break; case 's': /* swap usb device with another device */ case 'S': menu_swapusb(); From e8f00d07613a6338d136719835ec48b20a90ec27 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sun, 8 Sep 2024 12:27:38 -0700 Subject: [PATCH 02/18] Move rxaudiostats functions to ../res/res_usbradio.c, add 'ast_radio_' prefix to function names. Change rxaudiostats conf parameter name to 'checkrxaudio' and type to int, with value indicating GPIO# to use (or 0 to disable feature), validate value in hidhdwconfig() --- channels/chan_simpleusb.c | 203 ++++---------------------- channels/chan_usbradio.c | 5 +- configs/rpt/simpleusb.conf | 5 +- configs/rpt/usbradio.conf | 5 + configs/samples/simpleusb.conf.sample | 5 +- configs/samples/usbradio.conf.sample | 5 + include/asterisk/res_usbradio.h | 76 ++++++++++ res/res_usbradio.c | 87 +++++++++++ 8 files changed, 209 insertions(+), 182 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 7a32e72..ddd9d01 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -272,7 +272,6 @@ struct chan_simpleusb_pvt { int hid_io_ctcss; int hid_io_ctcss_loc; int hid_io_ptt; - int hid_io_clip_led; /* indicator to alert user of ADC clipping */ int hid_gpio_loc; int32_t hid_gpio_val; int32_t valid_gpios; @@ -315,7 +314,7 @@ struct chan_simpleusb_pvt { struct timeval tonetime; int toneflag; int duplex3; - int rxaudiostats; + int checkrxaudio; int32_t discfactor; int32_t discounterl; @@ -328,14 +327,7 @@ struct chan_simpleusb_pvt { char *gpios[GPIO_PINCOUNT]; char *pps[32]; - /* Rx audio (ADC) statistics variables. susb tune-menu "R" command displays - * stats data (peak, average, min, max levels and clipped sample count). - */ -#define AUDIO_STATS_LEN 50 /* number of 20mS frames. 50 => 1 second buf len */ - unsigned short maxbuf[AUDIO_STATS_LEN]; /* peak sample value per frame */ - unsigned short clipbuf[AUDIO_STATS_LEN]; /* number of clipped samples per frame */ - unsigned int pwrbuf[AUDIO_STATS_LEN]; /* total RMS power per frame */ - short rxaudiostats_index; /* Index within buffers, updated as frames received */ + struct rxaudiostatistics rxaudiostats; ast_mutex_t usblock; }; @@ -355,8 +347,8 @@ static struct chan_simpleusb_pvt simpleusb_default = { .rxondelay = 0, .txoffdelay = 0, .pager = PAGER_NONE, - .rxaudiostats = 1, - .rxaudiostats_index = 0 + .checkrxaudio = 1, + .rxaudiostats.index = 0 }; /* DECLARE FUNCTION PROTOTYPES */ @@ -380,8 +372,6 @@ static int simpleusb_setoption(struct ast_channel *chan, int option, void *data, static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *cmd); static void tune_write(struct chan_simpleusb_pvt *o); static int _send_tx_test_tone(int fd, struct chan_simpleusb_pvt *o, int ms, int intflag); -static void check_rx_audio(struct chan_simpleusb_pvt *o, short len); -static void print_rx_audio_stats(int fd, struct chan_simpleusb_pvt *o); static char *simpleusb_active; /* the active device */ @@ -574,7 +564,6 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 8; /* GPIO 4 is PTT */ - o->hid_io_clip_led = 0; /* No Clip LED on this HW */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } else if (o->hdwtype == 0) { //dudeusb @@ -585,7 +574,6 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 1; /* VOL UP External CTCSS */ o->hid_io_ctcss_loc = 0; /* VOL UP External CTCSS */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ - o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 0xfb; /* for GPIO 1,2,4,5,6,7,8 (5,6,7,8 for CM-119 only) */ } else if (o->hdwtype == 2) { //NHRC (N1KDO) (dudeusb w/o user GPIO) @@ -596,7 +584,6 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 1; /* VOL UP is External CTCSS */ o->hid_io_ctcss_loc = 0; /* VOL UP CTCSS */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ - o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 0; /* for GPIO 1,2,4 */ } else if (o->hdwtype == 3) { // custom version @@ -607,10 +594,15 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_io_ctcss = 2; /* GPIO 2 is External CTCSS */ o->hid_io_ctcss_loc = 1; /* is GPIO 2 */ o->hid_io_ptt = 4; /* GPIO 3 is PTT */ - o->hid_io_clip_led = 3; /* GPIO 4 is Clip LED */ o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } + /* validate checkrxaudio setting (Clip LED GPIO#) */ + if (o->checkrxaudio && (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1))))) + { + ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); + o->checkrxaudio = 0; + } o->hid_gpio_val = 0; for (i = 0; i < GPIO_PINCOUNT; i++) { /* skip if this one not specified */ @@ -2288,12 +2280,20 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) /* Check for ADC clipping and input audio statistics before any filtering is done. * FRAME_SIZE define refers to 8Ksps mono which is 160 samples per 20mS USB frame. - * check_rx_audio() takes the read buffer as received (48K stereo), extracts the - * mono 48K channel and checks amplitude and distortion characteristics. + * ast_radio_check_rx_audio() takes the read buffer as received (48K stereo), + * extracts the mono 48K channel, checks amplitude and distortion characteristics, + * and returns true if clipping was detected. */ - if(o->rxaudiostats) + if(o->checkrxaudio) { - check_rx_audio(o, 12 * FRAME_SIZE); + if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) + { + /* Set Clip LED GPIO pulsetimer if not already set */ + if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) + { + o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; + } + } } /* Downsample received audio from 48000 stereo to 8000 mono */ @@ -3353,12 +3353,12 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); break; } - if (!o->rxaudiostats) { - ast_cli(fd, "rxaudiostats is currently Disabled in simpleusb.conf\n"); + if (!o->checkrxaudio) { + ast_cli(fd, "checkrxaudio is currently Disabled in simpleusb.conf\n"); break; } for (;;) { - print_rx_audio_stats(fd, o); + ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); if (cmd[0] == 'A') break; if (ast_radio_poll_input(fd, 1000)) @@ -3698,7 +3698,7 @@ static struct chan_simpleusb_pvt *store_config(const struct ast_config *cfg, con CV_BOOL("deemphasis", o->deemphasis); CV_BOOL("preemphasis", o->preemphasis); CV_UINT("duplex3", o->duplex3); - CV_BOOL("rxaudiostats", o->rxaudiostats); + CV_UINT("checkrxaudio", o->checkrxaudio); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { @@ -4061,157 +4061,6 @@ static int unload_module(void) return 0; } -/*! - * \brief Detect ADC clipping, collect Rx audio statistics. - * - * If enabled by conf settings will set GPIO4 high for 500mS when clipping is - * detected. Nodes/URIs/audio interfaces can then light a Clip LED to alert users - * of excessive audio input levels. Because CM1xxx USB audio interface ICs have an - * internal mixer ahead of the ADC it is not possible within the interface board - * analog circuitry to detect clipping at the ADC input point, thus this function - * enables the raw ADC data to be checked. Clipping is detected by looking for - * large amplitude square waves (min. 3 samples in a row > 99% FS). - * - * Data collected can be displayed from the simpleusb-tune-menu 'R' option or AMI - * "susb tune menu-support a" function. This also shows average power levels which - * can be of further use in optimizing audio levels, compression, limiting, etc. - * In general, peak levels should be within 6-10dB of full-scale (0dBFS) and - * average signal power levels should be 6-12dB below peak levels. - * - * Should be passed the raw 48Ksps stereo USB frame read buffer before any - * filtering or downsampling has been done. Extracts the 48K mono channel and - * downsamples to 8Ksps (as is done in simpleusb_read() but without filtering). - * Signal power calculation takes the square of each sample to measure RMS power. - * For CPU efficiency no scaling is done here. (When stats data is printed the - * values are scaled to dBFS.) - * - * Audio parameters of interest include: - * - Peak signal level over a longer time period eg. 1+ seconds (dBFS) - * This defines headroom (dB) and potential for clipping - * - Min and max signal power levels averaged within each USB frame (dBFS) - * These define average dynamic range (dB) - * - Min and max signal power averaged over a longer time period (dBFS) - * These define total signal power and peak-to-average power ratio - * - * \author NR9V - * \param o Channel data structure - * \param len Length of data within o->simpleusb_read_buf - * \return None - */ -#define CLIP_SAMP_THRESH 0x7eb0 -#define CLIP_EVENT_MIN_SAMPLES 3 -#define CLIP_LED_HOLD_TIME_MS 500 -static void check_rx_audio(struct chan_simpleusb_pvt *o, short len) -{ - short *sbuf = (short *) o->simpleusb_read_buf; - unsigned short i, j, val, max=0, clip_cnt=0, seq_clips=0, last_clip=-1; - double pwr=0.0; - short buf[FRAME_SIZE]; - - if(len > 12 * FRAME_SIZE) - len = 12 * FRAME_SIZE; - if(o->rxaudiostats_index >= AUDIO_STATS_LEN) - o->rxaudiostats_index = 0; - /* Downsample from 48000 stereo to 8000 mono */ - for(i=10, j=0; i < len; i += 12) - { - buf[j++] = sbuf[i]; - } - len /= 12; - /* len should now be 160 */ - for(i=0; i < len; i++) - { - val = abs(buf[i]); - if(val) - { - if(val > max) - max = val; - pwr += (double) (val * val); - if(val > CLIP_SAMP_THRESH) - { - clip_cnt++; - if(last_clip >= 0 && last_clip + 1 == i) - seq_clips++; - last_clip = i; - } - } - } - o->maxbuf[o->rxaudiostats_index] = max; - o->pwrbuf[o->rxaudiostats_index] = (unsigned int) (pwr / (double)len); - o->clipbuf[o->rxaudiostats_index] = seq_clips; - /* Set Clip LED if clipping detected and LED not already set */ - if(seq_clips >= CLIP_EVENT_MIN_SAMPLES && o->hid_io_clip_led && - !o->hid_gpio_pulsetimer[o->hid_io_clip_led]) - { - o->hid_gpio_pulsetimer[o->hid_io_clip_led] = CLIP_LED_HOLD_TIME_MS; - } - if(++o->rxaudiostats_index >= AUDIO_STATS_LEN) - { - o->rxaudiostats_index = 0; - } -} - -/*! - * \brief Display receive audio statistics. - * - * Display the audio stats buffer data in normalized units. Peak value is the largest - * sample value seen in the past AUDIO_STATS_LEN audio frames (1 second default). - * Average, min, and max signal power levels are calculated from the total signal - * power buffer which contains total RMS power per 20mS frame. Avg Pwr is the average - * of the power values in the buffer, min and max are the lowest and highest average - * power levels within the buffer. ClipCnt is the count of audio clipping events - * detected. - * - * Example output message: - * RxAudioStats: Pk -2.1 Avg Pwr -32 Min -60 Max -12 dBFS ClipCnt 0 - * - * Results are scaled to double precision 0.0-1.0 and converted to log (dB) - * ie. 10*log10(scaledVal) for power levels. - * - * \author NR9V - * \param fd File descriptor to print to, or if 0 print using ast_verbose() - * \param o Channel data structure - * \return None - */ -static void print_rx_audio_stats(int fd, struct chan_simpleusb_pvt *o) -{ - unsigned int pk=0, pwr=0, i, minpwr=0x40000000, maxpwr=0, clipcnt=0; - double tpwr=0.0, dpk, dmin, dmax, scale; - char s1[100]; - - /* Peak = max(maxbuf)^2 - * Avg Pwr = avg(pwrbuf) - * Min = min(pwrbuf) - * Max = max(pwrbuf) - */ - for(i=0; i < AUDIO_STATS_LEN; i++) - { - if(o->maxbuf[i] > pk) - pk = o->maxbuf[i]; - pwr = o->pwrbuf[i]; - if(pwr < minpwr) - minpwr = pwr; - if(pwr > maxpwr) - maxpwr = pwr; - tpwr += pwr; - clipcnt += o->clipbuf[i]; - } - tpwr /= AUDIO_STATS_LEN; - /* Convert to dBFS / dB */ - scale = 1.0 / (double) (1 << 30); - dpk = (pk > 0.0) ? 10 * log10(pk * pk * scale) : -96.0; - tpwr = (tpwr > 0.0) ? 10 * log10(tpwr * scale) : -96.0; - dmin = minpwr ? 10 * log10(minpwr * scale) : -96.0; - dmax = maxpwr ? 10 * log10(maxpwr * scale) : -96.0; - /* Print stats */ - sprintf(s1, "RxAudioStats: Pk %5.1f Avg Pwr %3.0f Min %3.0f Max %3.0f dBFS ClipCnt %u", - dpk, tpwr, dmin, dmax, clipcnt); - if(fd) - ast_cli(fd, "%s\n", s1); - else - ast_verbose("%s\n", s1); -} - AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "SimpleUSB Radio Interface Channel Driver", .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 23a9815..9687e5d 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -399,6 +399,7 @@ struct chan_usbradio_pvt { struct timeval tonetime; int toneflag; int duplex3; + int checkrxaudio; int fever; int count_rssi_update; @@ -407,7 +408,9 @@ struct chan_usbradio_pvt { char *gpios[GPIO_PINCOUNT]; char *pps[32]; int sendvoter; - + + struct rxaudiostatistics rxaudiostats; + ast_mutex_t usblock; }; diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index 8fdc583..f545fa2 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -108,9 +108,10 @@ preemphasis = no ; Perform standard 6db/octave pre-emphasis ; duplex3 = 0 ; duplex 3 gain setting (0 to disable) -rxaudiostats = yes ; Enable receive audio statistics and ADC clip detection (no to disable). +checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting GPIO4 (if available) high for 500mS when clipping detected. + ; setting a GPIO (if available) high for 500mS when clipping detected. + ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. ;;; End of node-main template diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index 90d0201..9f98ff1 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -168,6 +168,11 @@ duplex = 0 ; Duplex 0,1 ; 1 - full duplex duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? +checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). + ; Enables tune-menu 'R' function. Supports audio interface Clip LED by + ; setting a GPIO (if available) high for 500mS when clipping detected. + ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. + ;;; End of node-main template ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index a82e910..5b369ee 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -90,9 +90,10 @@ ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) -;rxaudiostats = yes ; Enable receive audio statistics and ADC clip detection (no to disable). +;checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting GPIO4 (if available) high for 500mS when clipping detected. + ; setting a GPIO (if available) high for 500mS when clipping detected. + ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. ;gpioX=in ; define input/output pin GPIO(x) in,out0,out1 (where X {1..8}) (optional) diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index bacc1d4..916daed 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -154,6 +154,11 @@ ; 1 - full duplex ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? +;checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). + ; Enables tune-menu 'R' function. Supports audio interface Clip LED by + ; setting a GPIO (if available) high for 500mS when clipping detected. + ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. + ;gpioX=in ; define input/output pin GPIO(x) in,out0,out1 (where X {1..8}) (optional) ;pport=/dev/parport0 ; Specify parport device (optional) diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index d2206d9..4059b90 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -185,6 +185,18 @@ struct usbecho { struct qelem *q_prev; short data[FRAME_SIZE]; }; + +/* Rx audio (ADC) statistics variables. tune-menu "R" command displays + * stats data (peak, average, min, max levels and clipped sample count). + */ +#define AUDIO_STATS_LEN 50 /* number of 20mS frames. 50 => 1 second buf len */ +struct rxaudiostatistics { + unsigned short maxbuf[AUDIO_STATS_LEN]; /* peak sample value per frame */ + unsigned short clipbuf[AUDIO_STATS_LEN]; /* number of clipped samples per frame */ + unsigned int pwrbuf[AUDIO_STATS_LEN]; /* total RMS power per frame */ + short index; /* Index within buffers, updated as frames received */ +}; + /* * Message definition used in usb channel drivers. */ @@ -433,3 +445,67 @@ void ast_radio_time(time_t *second); */ struct timeval ast_radio_tvnow(void); +/*! + * \brief Detect ADC clipping, collect Rx audio statistics. + * + * If enabled by conf settings will set a GPIO high for 500mS when clipping is + * detected. Nodes/URIs/audio interfaces can then light a Clip LED to alert users + * of excessive audio input levels. Because CM1xxx USB audio interface ICs have an + * internal mixer ahead of the ADC it is not possible within the interface board + * analog circuitry to detect clipping at the ADC input point, thus this function + * enables the raw ADC data to be checked. Clipping is detected by looking for + * large amplitude square waves (min. 3 samples in a row > 99% FS). + * + * Data collected can be displayed from the simpleusb-tune-menu 'R' option or AMI + * "susb tune menu-support a" function. This also shows average power levels which + * can be of further use in optimizing audio levels, compression, limiting, etc. + * In general, peak levels should be within 6-10dB of full-scale (0dBFS) and + * average signal power levels should be 6-12dB below peak levels. + * + * Should be passed the raw 48Ksps stereo USB frame read buffer before any + * filtering or downsampling has been done. Extracts the 48K mono channel and + * downsamples to 8Ksps (as is done in simpleusb_read() but without filtering). + * Signal power calculation takes the square of each sample to measure RMS power. + * For CPU efficiency no scaling is done here. (When stats data is printed the + * values are scaled to dBFS.) + * + * Audio parameters of interest include: + * - Peak signal level over a longer time period eg. 1+ seconds (dBFS) + * This defines headroom (dB) and potential for clipping + * - Min and max signal power levels averaged within each USB frame (dBFS) + * These define average dynamic range (dB) + * - Min and max signal power averaged over a longer time period (dBFS) + * These define total signal power and peak-to-average power ratio + * + * \author NR9V + * \param sbuf Rx audio sample buffer + * \param o Rx Audio Stats data structure + * \param len Length of data in sbuf + * \return None + */ +#define CLIP_LED_HOLD_TIME_MS 500 +int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len); + +/*! + * \brief Display receive audio statistics. + * + * Display the audio stats buffer data in normalized units. Peak value is the largest + * sample value seen in the past AUDIO_STATS_LEN audio frames (1 second default). + * Average, min, and max signal power levels are calculated from the total signal + * power buffer which contains total RMS power per 20mS frame. Avg Pwr is the average + * of the power values in the buffer, min and max are the lowest and highest average + * power levels within the buffer. ClipCnt is the count of audio clipping events + * detected. + * + * Example output message: + * RxAudioStats: Pk -2.1 Avg Pwr -32 Min -60 Max -12 dBFS ClipCnt 0 + * + * Results are scaled to double precision 0.0-1.0 and converted to log (dB) + * ie. 10*log10(scaledVal) for power levels. + * + * \author NR9V + * \param fd File descriptor to print to, or if 0 print using ast_verbose() + * \param o Channel data structure + * \return None + */ +void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o); diff --git a/res/res_usbradio.c b/res/res_usbradio.c index c61a2d9..24ce952 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -732,6 +732,93 @@ struct timeval ast_radio_tvnow(void) return tv; } +#define CLIP_SAMP_THRESH 0x7eb0 +#define CLIP_EVENT_MIN_SAMPLES 3 +int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len) +{ + unsigned short i, j, val, max = 0, clip_cnt = 0, seq_clips = 0, last_clip = -1; + double pwr = 0.0; + short buf[FRAME_SIZE]; + + /* validate len and index */ + if (len > 12 * FRAME_SIZE) + len = 12 * FRAME_SIZE; + if (o->index >= AUDIO_STATS_LEN) + o->index = 0; + /* Downsample from 48000 stereo to 8000 mono */ + for (i = 10, j = 0; i < len; i += 12) + { + buf[j++] = sbuf[i]; + } + len /= 12; + /* len should now be 160 */ + for (i = 0; i < len; i++) + { + val = abs(buf[i]); + if (val) + { + if (val > max) + max = val; + pwr += (double) (val * val); + if (val > CLIP_SAMP_THRESH) + { + clip_cnt++; + if (last_clip >= 0 && last_clip + 1 == i) + seq_clips++; + last_clip = i; + } + } + } + o->maxbuf[o->index] = max; + o->pwrbuf[o->index] = (unsigned int) (pwr / (double)len); + o->clipbuf[o->index] = seq_clips; + if (++o->index >= AUDIO_STATS_LEN) + { + o->index = 0; + } + /* return 1 if clipping was detected */ + return (seq_clips >= CLIP_EVENT_MIN_SAMPLES); +} + +void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) +{ + unsigned int i, pk = 0, pwr = 0, minpwr = 0x40000000, maxpwr = 0, clipcnt = 0; + double dpk, dmin, dmax, scale, tpwr = 0.0; + char s1[100]; + + /* Peak = max(maxbuf)^2 + * Avg Pwr = avg(pwrbuf) + * Min = min(pwrbuf) + * Max = max(pwrbuf) + */ + for (i = 0; i < AUDIO_STATS_LEN; i++) + { + if (o->maxbuf[i] > pk) + pk = o->maxbuf[i]; + pwr = o->pwrbuf[i]; + if (pwr < minpwr) + minpwr = pwr; + if (pwr > maxpwr) + maxpwr = pwr; + tpwr += pwr; + clipcnt += o->clipbuf[i]; + } + tpwr /= AUDIO_STATS_LEN; + /* Convert to dBFS / dB */ + scale = 1.0 / (double) (1 << 30); + dpk = (pk > 0.0) ? 10 * log10(pk * pk * scale) : -96.0; + tpwr = (tpwr > 0.0) ? 10 * log10(tpwr * scale) : -96.0; + dmin = minpwr ? 10 * log10(minpwr * scale) : -96.0; + dmax = maxpwr ? 10 * log10(maxpwr * scale) : -96.0; + /* Print stats */ + sprintf(s1, "RxAudioStats: Pk %5.1f Avg Pwr %3.0f Min %3.0f Max %3.0f dBFS ClipCnt %u", + dpk, tpwr, dmin, dmax, clipcnt); + if (fd) + ast_cli(fd, "%s\n", s1); + else + ast_verbose("%s\n", s1); +} + static int load_module(void) { return 0; From 84644e55e98556d41875223e04b76d261fe9c1ce Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sun, 8 Sep 2024 15:52:40 -0700 Subject: [PATCH 03/18] Add Rx Audio Stats and Clip Detect feature support to chan_usbradio.c. Update susb tune-menu options to be consistent with that of usbradio. Default checkrxaudio conf param to off for usbradio. Add 'TBR' comments on some old sections of code that are present in both chan_simpleusb.c and chan_usbradio.c that will cause digital clipping of tx audio. --- channels/chan_simpleusb.c | 60 ++++++++++++++--------- channels/chan_usbradio.c | 71 +++++++++++++++++++++++++++- configs/rpt/usbradio.conf | 2 +- configs/samples/usbradio.conf.sample | 2 +- include/asterisk/res_usbradio.h | 18 +++---- utils/radio-tune-menu.c | 6 +++ utils/simpleusb-tune-menu.c | 4 +- 7 files changed, 125 insertions(+), 38 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index ddd9d01..e9cee9d 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -598,10 +598,17 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->valid_gpios = 1; /* for GPIO 1 */ } /* validate checkrxaudio setting (Clip LED GPIO#) */ - if (o->checkrxaudio && (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1))))) + if (o->checkrxaudio) { - ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); - o->checkrxaudio = 0; + if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) + { + ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); + o->checkrxaudio = 0; + } + else + { + o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ + } } o->hid_gpio_val = 0; for (i = 0; i < GPIO_PINCOUNT; i++) { @@ -2101,8 +2108,14 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) if (f1->datalen - src >= l) { /* enough to fill a frame */ memcpy(o->simpleusb_write_buf + o->simpleusb_write_dst, (char *) f1->data.ptr + src, l); + + /* TBR - below appears to be an attempt to match levels to the original CM108 + * IC which has been out of production for over 10 years. Scaling audio to + * 109.375% will result in clipping! Any adjustments for CM1xxx gain differences + * should be made in the mixer settings, not in the audio stream itself. + */ /* Adjust the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { register int v; sp = (short *) o->simpleusb_write_buf; @@ -2118,6 +2131,7 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) *sp++ = v; } } + sp = (short *) o->simpleusb_write_buf; sp1 = outbuf; doright = 1; @@ -3285,7 +3299,6 @@ static void tune_write(struct chan_simpleusb_pvt *o) * 1 - get node names that are configured in simpleusb.conf * 2 - print parameters * 3 - get node names that are configured in simpleusb.conf, except current device - * a - receive audio statistics display * b - receiver tune display * c - receive level * f - txa level @@ -3303,6 +3316,7 @@ static void tune_write(struct chan_simpleusb_pvt *o) * t - change rx on delay * u - change tx off delay * v - view cos, ctcss and ptt status + * y - receive audio statistics display * * \param fd Asterisk CLI fd * \param o Private struct. @@ -3347,24 +3361,6 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c } ast_cli(fd, "\n"); break; - case 'a': /* display receive audio statistics (interactive) */ - case 'A': /* display receive audio statistics (once only) */ - if (!o->hasusb) { - ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); - break; - } - if (!o->checkrxaudio) { - ast_cli(fd, "checkrxaudio is currently Disabled in simpleusb.conf\n"); - break; - } - for (;;) { - ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); - if (cmd[0] == 'A') - break; - if (ast_radio_poll_input(fd, 1000)) - break; - } - break; case 'b': /* receiver tune display */ if (!o->hasusb) { ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); @@ -3515,6 +3511,24 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c } tune_rxtx_status(fd, o); break; + case 'y': /* display receive audio statistics (interactive) */ + case 'Y': /* display receive audio statistics (once only) */ + if (!o->hasusb) { + ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); + break; + } + if (!o->checkrxaudio) { + ast_cli(fd, "checkrxaudio is currently Disabled in simpleusb.conf\n"); + break; + } + for (;;) { + ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); + if (cmd[0] == 'Y') + break; + if (ast_radio_poll_input(fd, 1000)) + break; + } + break; default: ast_cli(fd, "Invalid Command\n"); break; diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 9687e5d..474ac2a 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -430,6 +430,8 @@ static struct chan_usbradio_pvt usbradio_default = { .txoffdelay = 0, .area = 0, .rptnum = 0, + .checkrxaudio = 0, + .rxaudiostats.index = 0 }; /* DECLARE FUNCTION PROTOTYPES */ @@ -552,6 +554,19 @@ static int hidhdwconfig(struct chan_usbradio_pvt *o) o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } + /* validate checkrxaudio setting (Clip LED GPIO#) */ + if (o->checkrxaudio) + { + if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) + { + ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); + o->checkrxaudio = 0; + } + else + { + o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ + } + } o->hid_gpio_val = 0; for (i = 0; i < GPIO_PINCOUNT; i++) { /* skip if this one not specified */ @@ -2034,8 +2049,15 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) if (o->readerrs) { ast_log(LOG_WARNING, "USB read channel [%s] was not stuck.\n", o->name); } + + /* TBR - below appears to be an attempt to match levels to the original CM108 + * IC which has been out of production for over 10 years. Scaling all rx audio to + * 80% would result in a 20% loss in dynamic range, added quantization noise, and + * outgoing IAX audio reduced by 2dB. Any adjustments for CM1xxx IC gain differences + * should be made in the mixer settings, not in the audio stream itself. + */ /* Decrease the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { register short *sp = (short *) (o->usbradio_read_buf + o->readpos); register float v; register int i; @@ -2045,6 +2067,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } + o->readerrs = 0; o->readpos += res; if (o->readpos < sizeof(o->usbradio_read_buf)) { /* not enough samples */ @@ -2075,6 +2098,24 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } o->didpmrtx = 0; + /* Check for ADC clipping and input audio statistics before any filtering is done. + * FRAME_SIZE define refers to 8Ksps mono which is 160 samples per 20mS USB frame. + * ast_radio_check_rx_audio() takes the read buffer as received (48K stereo), + * extracts the mono 48K channel, checks amplitude and distortion characteristics, + * and returns true if clipping was detected. + */ + if(o->checkrxaudio) + { + if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) + { + /* Set Clip LED GPIO pulsetimer if not already set */ + if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) + { + o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; + } + } + } + PmrRx(o->pmrChan, (i16 *) (o->usbradio_read_buf + AST_FRIENDLY_OFFSET), (i16 *) (o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET), (i16 *) (o->usbradio_write_buf)); @@ -2099,8 +2140,13 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } #endif + /* TBR - below appears to be an attempt to match levels to the original CM108 + * IC which has been out of production for over 10 years. Scaling audio to 110% + * will result in clipping! Any adjustments for CM1xxx IC gain differences + * should be made in the mixer settings, not in the audio stream itself. + */ /* For the CM108 adjust the audio level */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { register short *sp = (short *) o->usbradio_write_buf; register float v; register int i; @@ -2115,6 +2161,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } + /* Write the received audio to the sound card */ soundcard_writeframe(o, (short *) o->usbradio_write_buf); @@ -3896,6 +3943,7 @@ static void _menu_txtone(int fd, struct chan_usbradio_pvt *o, const char *cstr) * v - view cos, ctcss and ptt status * w - change tx mixer a * x - change tx mixer b + * y - receive audio statistics display * * \param fd Asterisk CLI fd * \param o Private struct. @@ -4146,6 +4194,24 @@ static void tune_menusupport(int fd, struct chan_usbradio_pvt *o, const char *cm ast_cli(fd, "TX Mixer B is currently %d\n", o->txmixb); } break; + case 'y': /* display receive audio statistics (interactive) */ + case 'Y': /* display receive audio statistics (once only) */ + if (!o->hasusb) { + ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); + break; + } + if (!o->checkrxaudio) { + ast_cli(fd, "checkrxaudio is currently Disabled in usbradio.conf\n"); + break; + } + for (;;) { + ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); + if (cmd[0] == 'Y') + break; + if (ast_radio_poll_input(fd, 1000)) + break; + } + break; default: ast_cli(fd, "Invalid Command\n"); break; @@ -4870,6 +4936,7 @@ static struct chan_usbradio_pvt *store_config(const struct ast_config *cfg, cons CV_UINT("txlpf", o->txlpf); CV_UINT("txhpf", o->txhpf); CV_UINT("sendvoter", o->sendvoter); + CV_UINT("checkrxaudio", o->checkrxaudio); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index 9f98ff1..d7e6f6f 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -168,7 +168,7 @@ duplex = 0 ; Duplex 0,1 ; 1 - full duplex duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? -checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). +checkrxaudio = 0 ; Enable receive audio statistics and ADC clip detection (0 to disable). ; Enables tune-menu 'R' function. Supports audio interface Clip LED by ; setting a GPIO (if available) high for 500mS when clipping detected. ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index 916daed..d0a5c86 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -154,7 +154,7 @@ ; 1 - full duplex ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? -;checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). +;checkrxaudio = 0 ; Enable receive audio statistics and ADC clip detection (0 to disable). ; Enables tune-menu 'R' function. Supports audio interface Clip LED by ; setting a GPIO (if available) high for 500mS when clipping detected. ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index 4059b90..1b7462e 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -456,15 +456,15 @@ struct timeval ast_radio_tvnow(void); * enables the raw ADC data to be checked. Clipping is detected by looking for * large amplitude square waves (min. 3 samples in a row > 99% FS). * - * Data collected can be displayed from the simpleusb-tune-menu 'R' option or AMI - * "susb tune menu-support a" function. This also shows average power levels which - * can be of further use in optimizing audio levels, compression, limiting, etc. - * In general, peak levels should be within 6-10dB of full-scale (0dBFS) and - * average signal power levels should be 6-12dB below peak levels. - * - * Should be passed the raw 48Ksps stereo USB frame read buffer before any - * filtering or downsampling has been done. Extracts the 48K mono channel and - * downsamples to 8Ksps (as is done in simpleusb_read() but without filtering). + * Data collected can be displayed from the tune-menu 'R' option or AMI + * "[susb/radio] tune menu-support y" function. This also shows average power levels + * which can be useful for optimizing audio levels and compression/limiting. + * In general, peak levels should be within 3-10dB of full-scale (0dBFS) and + * average signal power levels should be 10-20dB below full-scale. + * + * Should be passed the raw 48Ksps stereo USB frame read buffer before any filtering + * or downsampling has been done. Extracts the 48K mono channel and downsamples to + * 8Ksps (as is done in [simpleusb/usbradio]_read() but without filtering). * Signal power calculation takes the square of each sample to measure RMS power. * For CPU efficiency no scaling is done here. (When stats data is printed the * values are scaled to dBFS.) diff --git a/utils/radio-tune-menu.c b/utils/radio-tune-menu.c index 22d6277..1b52d3b 100644 --- a/utils/radio-tune-menu.c +++ b/utils/radio-tune-menu.c @@ -54,6 +54,7 @@ * v - view cos, ctcss and ptt status * w - change tx mixer a * x - change tx mixer b + * y - receive audio statistics display * * Most of these commands take optional parameters to set values. * @@ -1043,6 +1044,7 @@ static void options_menu(void) printf("H) Change CTCSS From (currently '%s')\n", sd_signal_type[ctcssfrom]); printf("P) Print Current Parameter Values\n"); printf("O) Options Menu\n"); + printf("R) View Rx Audio Statistics\n"); printf("S) Swap Current USB device with another USB device\n"); printf("T) Toggle Transmit Test Tone/Keying (currently '%s')\n", (keying) ? "enabled" : "disabled"); printf("V) View COS, CTCSS and PTT Status\n"); @@ -1156,6 +1158,10 @@ static void options_menu(void) exit(255); } break; + case 'r': /* display receive audio statistics */ + case 'R': + astgetresp(COMMAND_PREFIX "tune menu-support y"); + break; case 's': /* swap usb device with another device */ case 'S': menu_swapusb(); diff --git a/utils/simpleusb-tune-menu.c b/utils/simpleusb-tune-menu.c index 4749bc8..afe814d 100644 --- a/utils/simpleusb-tune-menu.c +++ b/utils/simpleusb-tune-menu.c @@ -30,7 +30,6 @@ * 1 - get node names that are configured in simpleusb.conf * 2 - print parameters * 3 - get node names that are configured in simpleusb.conf, except current device - * a - receive audio statistics display * b - receiver tune display * c - receive level * f - txa level @@ -48,6 +47,7 @@ * t - change rx on delay * u - change tx off delay * v - view cos, ctcss and ptt status + * y - receive audio statistics display * * Most of these commands take optional parameters to set values. * @@ -945,7 +945,7 @@ static int astgetresp(char *cmd) break; case 'r': /* display receive audio statistics */ case 'R': - astgetresp(COMMAND_PREFIX "tune menu-support a"); + astgetresp(COMMAND_PREFIX "tune menu-support y"); break; case 's': /* swap usb device with another device */ case 'S': From efe18d82c21708173ff779798831aaf08a5cef36 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Mon, 9 Sep 2024 01:06:14 -0700 Subject: [PATCH 04/18] minor whitespace changes, changed one variable to a signed type. --- channels/chan_simpleusb.c | 2 +- channels/chan_usbradio.c | 2 +- include/asterisk/res_usbradio.h | 22 +++++++++++----------- res/res_usbradio.c | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index e9cee9d..36c84ff 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -2298,7 +2298,7 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if(o->checkrxaudio) + if (o->checkrxaudio) { if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 474ac2a..e82c05c 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -2104,7 +2104,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if(o->checkrxaudio) + if (o->checkrxaudio) { if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index 1b7462e..2be17a4 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -189,12 +189,12 @@ struct usbecho { /* Rx audio (ADC) statistics variables. tune-menu "R" command displays * stats data (peak, average, min, max levels and clipped sample count). */ -#define AUDIO_STATS_LEN 50 /* number of 20mS frames. 50 => 1 second buf len */ +#define AUDIO_STATS_LEN 50 /* number of 20mS frames. 50 => 1 second buf len */ struct rxaudiostatistics { - unsigned short maxbuf[AUDIO_STATS_LEN]; /* peak sample value per frame */ + unsigned short maxbuf[AUDIO_STATS_LEN]; /* peak sample value per frame */ unsigned short clipbuf[AUDIO_STATS_LEN]; /* number of clipped samples per frame */ - unsigned int pwrbuf[AUDIO_STATS_LEN]; /* total RMS power per frame */ - short index; /* Index within buffers, updated as frames received */ + unsigned int pwrbuf[AUDIO_STATS_LEN]; /* total RMS power per frame */ + short index; /* Index within buffers, updated as frames received */ }; /* @@ -477,11 +477,11 @@ struct timeval ast_radio_tvnow(void); * - Min and max signal power averaged over a longer time period (dBFS) * These define total signal power and peak-to-average power ratio * - * \author NR9V + * \author NR9V * \param sbuf Rx audio sample buffer - * \param o Rx Audio Stats data structure - * \param len Length of data in sbuf - * \return None + * \param o Rx Audio Stats data structure + * \param len Length of data in sbuf + * \return None */ #define CLIP_LED_HOLD_TIME_MS 500 int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len); @@ -503,9 +503,9 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len * Results are scaled to double precision 0.0-1.0 and converted to log (dB) * ie. 10*log10(scaledVal) for power levels. * - * \author NR9V + * \author NR9V * \param fd File descriptor to print to, or if 0 print using ast_verbose() - * \param o Channel data structure - * \return None + * \param o Channel data structure + * \return None */ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o); diff --git a/res/res_usbradio.c b/res/res_usbradio.c index 24ce952..fa8f8ff 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -736,9 +736,9 @@ struct timeval ast_radio_tvnow(void) #define CLIP_EVENT_MIN_SAMPLES 3 int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len) { - unsigned short i, j, val, max = 0, clip_cnt = 0, seq_clips = 0, last_clip = -1; + unsigned short i, j, val, max = 0, clip_cnt = 0, seq_clips = 0; double pwr = 0.0; - short buf[FRAME_SIZE]; + short buf[FRAME_SIZE], last_clip = -1; /* validate len and index */ if (len > 12 * FRAME_SIZE) @@ -806,7 +806,7 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) tpwr /= AUDIO_STATS_LEN; /* Convert to dBFS / dB */ scale = 1.0 / (double) (1 << 30); - dpk = (pk > 0.0) ? 10 * log10(pk * pk * scale) : -96.0; + dpk = (pk > 0.0) ? 10 * log10(pk * pk * scale) : -96.0; tpwr = (tpwr > 0.0) ? 10 * log10(tpwr * scale) : -96.0; dmin = minpwr ? 10 * log10(minpwr * scale) : -96.0; dmax = maxpwr ? 10 * log10(maxpwr * scale) : -96.0; From 55cf18ada17ad143c1e3c8d7be04c8e97f9a7c76 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Mon, 9 Sep 2024 13:28:43 -0700 Subject: [PATCH 05/18] Removed unused variable. --- include/asterisk/res_usbradio.h | 10 +++++----- res/res_usbradio.c | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index 2be17a4..b0b1671 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -477,11 +477,11 @@ struct timeval ast_radio_tvnow(void); * - Min and max signal power averaged over a longer time period (dBFS) * These define total signal power and peak-to-average power ratio * - * \author NR9V - * \param sbuf Rx audio sample buffer - * \param o Rx Audio Stats data structure - * \param len Length of data in sbuf - * \return None + * \author NR9V + * \param sbuf Rx audio sample buffer + * \param o Rx Audio Stats data structure + * \param len Length of data in sbuf + * \return None */ #define CLIP_LED_HOLD_TIME_MS 500 int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len); diff --git a/res/res_usbradio.c b/res/res_usbradio.c index fa8f8ff..955c4c6 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -736,7 +736,7 @@ struct timeval ast_radio_tvnow(void) #define CLIP_EVENT_MIN_SAMPLES 3 int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len) { - unsigned short i, j, val, max = 0, clip_cnt = 0, seq_clips = 0; + unsigned short i, j, val, max = 0, seq_clips = 0; double pwr = 0.0; short buf[FRAME_SIZE], last_clip = -1; @@ -762,7 +762,6 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len pwr += (double) (val * val); if (val > CLIP_SAMP_THRESH) { - clip_cnt++; if (last_clip >= 0 && last_clip + 1 == i) seq_clips++; last_clip = i; From 22bc47fb1f0ae604e414975b66f5ee1f70f71fdd Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 09:41:47 -0700 Subject: [PATCH 06/18] Add comments, update whitespace (if/for opening braces on same line) --- channels/chan_simpleusb.c | 20 +++++++------------- channels/chan_usbradio.c | 20 +++++++------------- include/asterisk/res_usbradio.h | 2 +- res/res_usbradio.c | 18 ++++++------------ 4 files changed, 21 insertions(+), 39 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 36c84ff..dae7398 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -314,7 +314,7 @@ struct chan_simpleusb_pvt { struct timeval tonetime; int toneflag; int duplex3; - int checkrxaudio; + int checkrxaudio; /* enables RxAudioStats feature & Clip LED output on specified GPIO# */ int32_t discfactor; int32_t discounterl; @@ -598,15 +598,12 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->valid_gpios = 1; /* for GPIO 1 */ } /* validate checkrxaudio setting (Clip LED GPIO#) */ - if (o->checkrxaudio) - { - if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) - { + if (o->checkrxaudio) { + if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) { ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); o->checkrxaudio = 0; } - else - { + else { o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ } } @@ -2298,13 +2295,10 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if (o->checkrxaudio) - { - if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) - { + if (o->checkrxaudio) { + if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { /* Set Clip LED GPIO pulsetimer if not already set */ - if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) - { + if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) { o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; } } diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index e82c05c..b179764 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -399,7 +399,7 @@ struct chan_usbradio_pvt { struct timeval tonetime; int toneflag; int duplex3; - int checkrxaudio; + int checkrxaudio; /* enables RxAudioStats feature & Clip LED output on specified GPIO# */ int fever; int count_rssi_update; @@ -555,15 +555,12 @@ static int hidhdwconfig(struct chan_usbradio_pvt *o) o->valid_gpios = 1; /* for GPIO 1 */ } /* validate checkrxaudio setting (Clip LED GPIO#) */ - if (o->checkrxaudio) - { - if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) - { + if (o->checkrxaudio) { + if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) { ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); o->checkrxaudio = 0; } - else - { + else { o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ } } @@ -2104,13 +2101,10 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if (o->checkrxaudio) - { - if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) - { + if (o->checkrxaudio) { + if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { /* Set Clip LED GPIO pulsetimer if not already set */ - if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) - { + if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) { o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; } } diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index b0b1671..de36736 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -481,7 +481,7 @@ struct timeval ast_radio_tvnow(void); * \param sbuf Rx audio sample buffer * \param o Rx Audio Stats data structure * \param len Length of data in sbuf - * \return None + * \return 1 if clipping detected, 0 otherwise */ #define CLIP_LED_HOLD_TIME_MS 500 int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len); diff --git a/res/res_usbradio.c b/res/res_usbradio.c index 955c4c6..1ba63dc 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -746,22 +746,18 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len if (o->index >= AUDIO_STATS_LEN) o->index = 0; /* Downsample from 48000 stereo to 8000 mono */ - for (i = 10, j = 0; i < len; i += 12) - { + for (i = 10, j = 0; i < len; i += 12) { buf[j++] = sbuf[i]; } len /= 12; /* len should now be 160 */ - for (i = 0; i < len; i++) - { + for (i = 0; i < len; i++) { val = abs(buf[i]); - if (val) - { + if (val) { if (val > max) max = val; pwr += (double) (val * val); - if (val > CLIP_SAMP_THRESH) - { + if (val > CLIP_SAMP_THRESH) { if (last_clip >= 0 && last_clip + 1 == i) seq_clips++; last_clip = i; @@ -771,8 +767,7 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len o->maxbuf[o->index] = max; o->pwrbuf[o->index] = (unsigned int) (pwr / (double)len); o->clipbuf[o->index] = seq_clips; - if (++o->index >= AUDIO_STATS_LEN) - { + if (++o->index >= AUDIO_STATS_LEN) { o->index = 0; } /* return 1 if clipping was detected */ @@ -790,8 +785,7 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) * Min = min(pwrbuf) * Max = max(pwrbuf) */ - for (i = 0; i < AUDIO_STATS_LEN; i++) - { + for (i = 0; i < AUDIO_STATS_LEN; i++) { if (o->maxbuf[i] > pk) pk = o->maxbuf[i]; pwr = o->pwrbuf[i]; From 74dd2e64854185b20225d474b14d886497c6c201 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 09:57:46 -0700 Subject: [PATCH 07/18] Add braces around single if statements. --- channels/chan_simpleusb.c | 6 ++++-- channels/chan_usbradio.c | 6 ++++-- res/res_usbradio.c | 27 ++++++++++++++++++--------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index dae7398..c4a3335 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -3517,10 +3517,12 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c } for (;;) { ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); - if (cmd[0] == 'Y') + if (cmd[0] == 'Y') { break; - if (ast_radio_poll_input(fd, 1000)) + } + if (ast_radio_poll_input(fd, 1000)) { break; + } } break; default: diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index b179764..15b49b7 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -4200,10 +4200,12 @@ static void tune_menusupport(int fd, struct chan_usbradio_pvt *o, const char *cm } for (;;) { ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); - if (cmd[0] == 'Y') + if (cmd[0] == 'Y') { break; - if (ast_radio_poll_input(fd, 1000)) + } + if (ast_radio_poll_input(fd, 1000)) { break; + } } break; default: diff --git a/res/res_usbradio.c b/res/res_usbradio.c index 1ba63dc..dde0417 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -741,10 +741,12 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len short buf[FRAME_SIZE], last_clip = -1; /* validate len and index */ - if (len > 12 * FRAME_SIZE) + if (len > 12 * FRAME_SIZE) { len = 12 * FRAME_SIZE; - if (o->index >= AUDIO_STATS_LEN) + } + if (o->index >= AUDIO_STATS_LEN) { o->index = 0; + } /* Downsample from 48000 stereo to 8000 mono */ for (i = 10, j = 0; i < len; i += 12) { buf[j++] = sbuf[i]; @@ -754,12 +756,14 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len for (i = 0; i < len; i++) { val = abs(buf[i]); if (val) { - if (val > max) + if (val > max) { max = val; + } pwr += (double) (val * val); if (val > CLIP_SAMP_THRESH) { - if (last_clip >= 0 && last_clip + 1 == i) + if (last_clip >= 0 && last_clip + 1 == i) { seq_clips++; + } last_clip = i; } } @@ -786,13 +790,16 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) * Max = max(pwrbuf) */ for (i = 0; i < AUDIO_STATS_LEN; i++) { - if (o->maxbuf[i] > pk) + if (o->maxbuf[i] > pk) { pk = o->maxbuf[i]; + } pwr = o->pwrbuf[i]; - if (pwr < minpwr) + if (pwr < minpwr) { minpwr = pwr; - if (pwr > maxpwr) + } + if (pwr > maxpwr) { maxpwr = pwr; + } tpwr += pwr; clipcnt += o->clipbuf[i]; } @@ -806,10 +813,12 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) /* Print stats */ sprintf(s1, "RxAudioStats: Pk %5.1f Avg Pwr %3.0f Min %3.0f Max %3.0f dBFS ClipCnt %u", dpk, tpwr, dmin, dmax, clipcnt); - if (fd) + if (fd) { ast_cli(fd, "%s\n", s1); - else + } + else { ast_verbose("%s\n", s1); + } } static int load_module(void) From 7d66fc6b1c2b8dec3857a67e818f3bffe5b4b189 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 12:59:31 -0700 Subject: [PATCH 08/18] Rename checkrxaudio parameter to clipledgpio (which specifies the GPIO pin to use for the ADC Clip Detect Feature (0=disabled)). Set default value of clipledgpio to disabled in chan_[simpleusb|usbradio].c (so writing to a GPIO won't be automatically enabled during ASL updates), but enabled in configs/rpt/simpleusb.conf. --- channels/chan_simpleusb.c | 34 +++++++++++------------ channels/chan_usbradio.c | 39 +++++++++++++-------------- configs/rpt/simpleusb.conf | 7 +++-- configs/rpt/usbradio.conf | 7 +++-- configs/samples/simpleusb.conf.sample | 7 +++-- configs/samples/usbradio.conf.sample | 7 +++-- 6 files changed, 47 insertions(+), 54 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index c4a3335..d78741a 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -314,7 +314,7 @@ struct chan_simpleusb_pvt { struct timeval tonetime; int toneflag; int duplex3; - int checkrxaudio; /* enables RxAudioStats feature & Clip LED output on specified GPIO# */ + int clipledgpio; /* enables ADC Clip Detect feature to output on a specified GPIO# */ int32_t discfactor; int32_t discounterl; @@ -347,7 +347,7 @@ static struct chan_simpleusb_pvt simpleusb_default = { .rxondelay = 0, .txoffdelay = 0, .pager = PAGER_NONE, - .checkrxaudio = 1, + .clipledgpio = 0, .rxaudiostats.index = 0 }; @@ -597,14 +597,14 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } - /* validate checkrxaudio setting (Clip LED GPIO#) */ - if (o->checkrxaudio) { - if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) { - ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); - o->checkrxaudio = 0; + /* validate clipledgpio setting (Clip LED GPIO#) */ + if (o->clipledgpio) { + if (o->clipledgpio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->clipledgpio - 1)))) { + ast_log(LOG_ERROR, "Channel %s: clipledgpio = GPIO%d not supported\n", o->name, o->clipledgpio); + o->clipledgpio = 0; } else { - o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ + o->hid_gpio_ctl |= 1 << (o->clipledgpio - 1); /* confirm Clip LED GPIO set to output mode */ } } o->hid_gpio_val = 0; @@ -2111,8 +2111,9 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) * 109.375% will result in clipping! Any adjustments for CM1xxx gain differences * should be made in the mixer settings, not in the audio stream itself. */ +#if 0 /* Adjust the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { + if (o->devtype != C108_PRODUCT_ID) { register int v; sp = (short *) o->simpleusb_write_buf; @@ -2128,6 +2129,7 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) *sp++ = v; } } +#endif sp = (short *) o->simpleusb_write_buf; sp1 = outbuf; @@ -2295,11 +2297,11 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if (o->checkrxaudio) { - if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { + if (ast_radio_check_rx_audio((short *) o->simpleusb_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { + if (o->clipledgpio) { /* Set Clip LED GPIO pulsetimer if not already set */ - if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) { - o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; + if (!o->hid_gpio_pulsetimer[o->clipledgpio - 1]) { + o->hid_gpio_pulsetimer[o->clipledgpio - 1] = CLIP_LED_HOLD_TIME_MS; } } } @@ -3511,10 +3513,6 @@ static void tune_menusupport(int fd, struct chan_simpleusb_pvt *o, const char *c ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); break; } - if (!o->checkrxaudio) { - ast_cli(fd, "checkrxaudio is currently Disabled in simpleusb.conf\n"); - break; - } for (;;) { ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); if (cmd[0] == 'Y') { @@ -3708,7 +3706,7 @@ static struct chan_simpleusb_pvt *store_config(const struct ast_config *cfg, con CV_BOOL("deemphasis", o->deemphasis); CV_BOOL("preemphasis", o->preemphasis); CV_UINT("duplex3", o->duplex3); - CV_UINT("checkrxaudio", o->checkrxaudio); + CV_UINT("clipledgpio", o->clipledgpio); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 15b49b7..e606e09 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -399,7 +399,7 @@ struct chan_usbradio_pvt { struct timeval tonetime; int toneflag; int duplex3; - int checkrxaudio; /* enables RxAudioStats feature & Clip LED output on specified GPIO# */ + int clipledgpio; /* enables ADC Clip Detect feature to output on a specified GPIO# */ int fever; int count_rssi_update; @@ -430,7 +430,7 @@ static struct chan_usbradio_pvt usbradio_default = { .txoffdelay = 0, .area = 0, .rptnum = 0, - .checkrxaudio = 0, + .clipledgpio = 0, .rxaudiostats.index = 0 }; @@ -554,14 +554,14 @@ static int hidhdwconfig(struct chan_usbradio_pvt *o) o->hid_gpio_loc = 1; /* For ALL GPIO */ o->valid_gpios = 1; /* for GPIO 1 */ } - /* validate checkrxaudio setting (Clip LED GPIO#) */ - if (o->checkrxaudio) { - if (o->checkrxaudio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->checkrxaudio - 1)))) { - ast_log(LOG_ERROR, "Channel %s: checkrxaudio = GPIO%d not supported\n", o->name, o->checkrxaudio); - o->checkrxaudio = 0; + /* validate clipledgpio setting (Clip LED GPIO#) */ + if (o->clipledgpio) { + if (o->clipledgpio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->clipledgpio - 1)))) { + ast_log(LOG_ERROR, "Channel %s: clipledgpio = GPIO%d not supported\n", o->name, o->clipledgpio); + o->clipledgpio = 0; } else { - o->hid_gpio_ctl |= 1 << (o->checkrxaudio - 1); /* confirm Clip LED GPIO set to output mode */ + o->hid_gpio_ctl |= 1 << (o->clipledgpio - 1); /* confirm Clip LED GPIO set to output mode */ } } o->hid_gpio_val = 0; @@ -2053,8 +2053,9 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) * outgoing IAX audio reduced by 2dB. Any adjustments for CM1xxx IC gain differences * should be made in the mixer settings, not in the audio stream itself. */ +#if 0 /* Decrease the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { + if (o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) (o->usbradio_read_buf + o->readpos); register float v; register int i; @@ -2064,6 +2065,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } +#endif o->readerrs = 0; o->readpos += res; @@ -2101,11 +2103,11 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) * extracts the mono 48K channel, checks amplitude and distortion characteristics, * and returns true if clipping was detected. */ - if (o->checkrxaudio) { - if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { + if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { + if (o->clipledgpio) { /* Set Clip LED GPIO pulsetimer if not already set */ - if (!o->hid_gpio_pulsetimer[o->checkrxaudio - 1]) { - o->hid_gpio_pulsetimer[o->checkrxaudio - 1] = CLIP_LED_HOLD_TIME_MS; + if (!o->hid_gpio_pulsetimer[o->clipledgpio - 1]) { + o->hid_gpio_pulsetimer[o->clipledgpio - 1] = CLIP_LED_HOLD_TIME_MS; } } } @@ -2139,8 +2141,9 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) * will result in clipping! Any adjustments for CM1xxx IC gain differences * should be made in the mixer settings, not in the audio stream itself. */ +#if 0 /* For the CM108 adjust the audio level */ - if (o->devtype != C108_PRODUCT_ID && !o->checkrxaudio) { + if (o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) o->usbradio_write_buf; register float v; register int i; @@ -2155,7 +2158,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } - +#endif /* Write the received audio to the sound card */ soundcard_writeframe(o, (short *) o->usbradio_write_buf); @@ -4194,10 +4197,6 @@ static void tune_menusupport(int fd, struct chan_usbradio_pvt *o, const char *cm ast_cli(fd, USB_UNASSIGNED_FMT, o->name, o->devstr); break; } - if (!o->checkrxaudio) { - ast_cli(fd, "checkrxaudio is currently Disabled in usbradio.conf\n"); - break; - } for (;;) { ast_radio_print_rx_audio_stats(fd, &o->rxaudiostats); if (cmd[0] == 'Y') { @@ -4932,7 +4931,7 @@ static struct chan_usbradio_pvt *store_config(const struct ast_config *cfg, cons CV_UINT("txlpf", o->txlpf); CV_UINT("txhpf", o->txhpf); CV_UINT("sendvoter", o->sendvoter); - CV_UINT("checkrxaudio", o->checkrxaudio); + CV_UINT("clipledgpio", o->clipledgpio); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index f545fa2..baadfea 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -108,10 +108,9 @@ preemphasis = no ; Perform standard 6db/octave pre-emphasis ; duplex3 = 0 ; duplex 3 gain setting (0 to disable) -checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). - ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting a GPIO (if available) high for 500mS when clipping detected. - ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. +clipledgpio = 1 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). + ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS + ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) ;;; End of node-main template diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index d7e6f6f..f81f54b 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -168,10 +168,9 @@ duplex = 0 ; Duplex 0,1 ; 1 - full duplex duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? -checkrxaudio = 0 ; Enable receive audio statistics and ADC clip detection (0 to disable). - ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting a GPIO (if available) high for 500mS when clipping detected. - ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. +clipledgpio = 0 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). + ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS + ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) ;;; End of node-main template diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index 5b369ee..8ec0f71 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -90,10 +90,9 @@ ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) -;checkrxaudio = 1 ; Enable receive audio statistics and ADC clip detection (0 to disable). - ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting a GPIO (if available) high for 500mS when clipping detected. - ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. +;clipledgpio = 1 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). + ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS + ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) ;gpioX=in ; define input/output pin GPIO(x) in,out0,out1 (where X {1..8}) (optional) diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index d0a5c86..2e9b141 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -154,10 +154,9 @@ ; 1 - full duplex ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) ??? -;checkrxaudio = 0 ; Enable receive audio statistics and ADC clip detection (0 to disable). - ; Enables tune-menu 'R' function. Supports audio interface Clip LED by - ; setting a GPIO (if available) high for 500mS when clipping detected. - ; parameter value = GPIO# to use, eg. checkrxaudio = 1 will use GPIO1. +;clipledgpio = 0 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). + ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS + ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) ;gpioX=in ; define input/output pin GPIO(x) in,out0,out1 (where X {1..8}) (optional) From 217634be4e061c234be762c928b84717a6c49756 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 13:23:20 -0700 Subject: [PATCH 09/18] whitespace updates. --- channels/chan_simpleusb.c | 3 +-- channels/chan_usbradio.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index d78741a..6aaba7d 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -602,8 +602,7 @@ static int hidhdwconfig(struct chan_simpleusb_pvt *o) if (o->clipledgpio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->clipledgpio - 1)))) { ast_log(LOG_ERROR, "Channel %s: clipledgpio = GPIO%d not supported\n", o->name, o->clipledgpio); o->clipledgpio = 0; - } - else { + } else { o->hid_gpio_ctl |= 1 << (o->clipledgpio - 1); /* confirm Clip LED GPIO set to output mode */ } } diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index e606e09..a07f558 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -559,8 +559,7 @@ static int hidhdwconfig(struct chan_usbradio_pvt *o) if (o->clipledgpio >= GPIO_PINCOUNT || !(o->valid_gpios & (1 << (o->clipledgpio - 1)))) { ast_log(LOG_ERROR, "Channel %s: clipledgpio = GPIO%d not supported\n", o->name, o->clipledgpio); o->clipledgpio = 0; - } - else { + } else { o->hid_gpio_ctl |= 1 << (o->clipledgpio - 1); /* confirm Clip LED GPIO set to output mode */ } } From c1d0d8801e763352b7227fabb5e89545353abc42 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 14:09:59 -0700 Subject: [PATCH 10/18] set clipledgpio = 0 in configs/samples/simpleusb.conf.sample. --- configs/samples/simpleusb.conf.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index 8ec0f71..6e4716a 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -90,7 +90,7 @@ ;duplex3 = 0 ; duplex 3 gain setting (0 to disable) -;clipledgpio = 1 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). +;clipledgpio = 0 ; Enable ADC Clip Detect feature to use a GPIO output (0 to disable). ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) From a68ca64385364ecb327cae423728ceefbedbb8ac Mon Sep 17 00:00:00 2001 From: David Gleason Date: Thu, 12 Sep 2024 14:30:08 -0700 Subject: [PATCH 11/18] whitespace cleanup. --- res/res_usbradio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/res_usbradio.c b/res/res_usbradio.c index dde0417..0291ca6 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -815,8 +815,7 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) dpk, tpwr, dmin, dmax, clipcnt); if (fd) { ast_cli(fd, "%s\n", s1); - } - else { + } else { ast_verbose("%s\n", s1); } } From 67859d1ce9eb33f083f6ab5de729ee2c5bc147b0 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sat, 14 Sep 2024 21:58:20 -0700 Subject: [PATCH 12/18] re-enable scaling code in chan_simplusb and usbradio that scales the raw Tx audio by 1.1 and (usbradio only) scales raw Rx audio by 0.8. (This code should marked TBR should be removed asap as described in issue #399.) Move call to check_rx_audio() to above where the 0.8 rx audio scaling is done in chan_usbradio.c. --- channels/chan_simpleusb.c | 10 +++--- channels/chan_usbradio.c | 64 +++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 6aaba7d..987d3d6 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -2105,12 +2105,12 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) /* enough to fill a frame */ memcpy(o->simpleusb_write_buf + o->simpleusb_write_dst, (char *) f1->data.ptr + src, l); - /* TBR - below appears to be an attempt to match levels to the original CM108 - * IC which has been out of production for over 10 years. Scaling audio to - * 109.375% will result in clipping! Any adjustments for CM1xxx gain differences - * should be made in the mixer settings, not in the audio stream itself. + /* TBR - below is an attempt to match levels to the original CM108 IC which has + * been out of production for over 10 years. Scaling audio to 109.375% will + * result in clipping! Any adjustments for CM1xxx gain differences should be + * made in the mixer settings, not in the audio stream. */ -#if 0 +#if 1 /* Adjust the audio level for CM119 A/B devices */ if (o->devtype != C108_PRODUCT_ID) { register int v; diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index a07f558..9c68547 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -2046,13 +2046,34 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) ast_log(LOG_WARNING, "USB read channel [%s] was not stuck.\n", o->name); } - /* TBR - below appears to be an attempt to match levels to the original CM108 - * IC which has been out of production for over 10 years. Scaling all rx audio to - * 80% would result in a 20% loss in dynamic range, added quantization noise, and - * outgoing IAX audio reduced by 2dB. Any adjustments for CM1xxx IC gain differences - * should be made in the mixer settings, not in the audio stream itself. + o->readerrs = 0; + o->readpos += res; + if (o->readpos < sizeof(o->usbradio_read_buf)) { /* not enough samples */ + return &ast_null_frame; + } + + /* Check for ADC clipping and input audio statistics before any filtering is done. + * FRAME_SIZE define refers to 8Ksps mono which is 160 samples per 20mS USB frame. + * ast_radio_check_rx_audio() takes the read buffer as received (48K stereo), + * extracts the mono 48K channel, checks amplitude and distortion characteristics, + * and returns true if clipping was detected. */ -#if 0 + if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { + if (o->clipledgpio) { + /* Set Clip LED GPIO pulsetimer if not already set */ + if (!o->hid_gpio_pulsetimer[o->clipledgpio - 1]) { + o->hid_gpio_pulsetimer[o->clipledgpio - 1] = CLIP_LED_HOLD_TIME_MS; + } + } + } + + /* TBR - below is an attempt to match levels to the original CM108 IC which has been + * out of production for over 10 years. Scaling all rx audio to 80% results in a 20% + * loss in dynamic range, added quantization noise, a 2dB reduction in outgoing IAX + * audio levels, and inconsistency with Simpleusb. Adjustments for CM1xxx IC gain + * differences should be made in the mixer settings, not in the audio stream. + */ +#if 1 /* Decrease the audio level for CM119 A/B devices */ if (o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) (o->usbradio_read_buf + o->readpos); @@ -2066,12 +2087,6 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } #endif - o->readerrs = 0; - o->readpos += res; - if (o->readpos < sizeof(o->usbradio_read_buf)) { /* not enough samples */ - return &ast_null_frame; - } - #if 1 if (o->txkeyed || o->txtestkey || o->echoing) { if (!o->pmrChan->txPttIn) { @@ -2096,21 +2111,6 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } o->didpmrtx = 0; - /* Check for ADC clipping and input audio statistics before any filtering is done. - * FRAME_SIZE define refers to 8Ksps mono which is 160 samples per 20mS USB frame. - * ast_radio_check_rx_audio() takes the read buffer as received (48K stereo), - * extracts the mono 48K channel, checks amplitude and distortion characteristics, - * and returns true if clipping was detected. - */ - if (ast_radio_check_rx_audio((short *) o->usbradio_read_buf, &o->rxaudiostats, 12 * FRAME_SIZE)) { - if (o->clipledgpio) { - /* Set Clip LED GPIO pulsetimer if not already set */ - if (!o->hid_gpio_pulsetimer[o->clipledgpio - 1]) { - o->hid_gpio_pulsetimer[o->clipledgpio - 1] = CLIP_LED_HOLD_TIME_MS; - } - } - } - PmrRx(o->pmrChan, (i16 *) (o->usbradio_read_buf + AST_FRIENDLY_OFFSET), (i16 *) (o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET), (i16 *) (o->usbradio_write_buf)); @@ -2135,12 +2135,12 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } #endif - /* TBR - below appears to be an attempt to match levels to the original CM108 - * IC which has been out of production for over 10 years. Scaling audio to 110% - * will result in clipping! Any adjustments for CM1xxx IC gain differences - * should be made in the mixer settings, not in the audio stream itself. + /* TBR - below is an attempt to match levels to the original CM108 IC which has been + * out of production for over 10 years. Scaling audio to 110% will result in clipping! + * Any adjustments for CM1xxx IC gain differences should be made in the mixer + * settings, not in the audio stream. */ -#if 0 +#if 1 /* For the CM108 adjust the audio level */ if (o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) o->usbradio_write_buf; From c9d1b41739b4551a6029724d9256d92d5f5658dc Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sun, 15 Sep 2024 12:30:06 -0700 Subject: [PATCH 13/18] consider 0 a valid file descriptor value in ast_radio_print_rx_audio_stats() per review comment --- include/asterisk/res_usbradio.h | 2 +- res/res_usbradio.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index de36736..6dfd2b6 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -504,7 +504,7 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len * ie. 10*log10(scaledVal) for power levels. * * \author NR9V - * \param fd File descriptor to print to, or if 0 print using ast_verbose() + * \param fd File descriptor to print to, or if -1 print using ast_verbose() * \param o Channel data structure * \return None */ diff --git a/res/res_usbradio.c b/res/res_usbradio.c index 0291ca6..c7f9175 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -813,7 +813,7 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) /* Print stats */ sprintf(s1, "RxAudioStats: Pk %5.1f Avg Pwr %3.0f Min %3.0f Max %3.0f dBFS ClipCnt %u", dpk, tpwr, dmin, dmax, clipcnt); - if (fd) { + if (fd >= 0) { ast_cli(fd, "%s\n", s1); } else { ast_verbose("%s\n", s1); From 5a30d094e35dbe6d80716ae49315a658f8f2f843 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sun, 15 Sep 2024 12:41:26 -0700 Subject: [PATCH 14/18] update ast_radio_print_rx_audio_stats() function header comment to clarify that it will print using ast_verbose() if passed in fd < 0 --- include/asterisk/res_usbradio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/asterisk/res_usbradio.h b/include/asterisk/res_usbradio.h index 6dfd2b6..792290b 100644 --- a/include/asterisk/res_usbradio.h +++ b/include/asterisk/res_usbradio.h @@ -504,7 +504,7 @@ int ast_radio_check_rx_audio(short *sbuf, struct rxaudiostatistics *o, short len * ie. 10*log10(scaledVal) for power levels. * * \author NR9V - * \param fd File descriptor to print to, or if -1 print using ast_verbose() + * \param fd File descriptor to print to, or if < 0 print using ast_verbose() * \param o Channel data structure * \return None */ From 70683d2adae5eaf90768a4b2c9a66970ab7f325a Mon Sep 17 00:00:00 2001 From: David Gleason Date: Fri, 4 Oct 2024 21:34:41 -0700 Subject: [PATCH 15/18] Fix issue #415 --- channels/chan_usbradio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index 9c68547..f97db4e 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -2076,7 +2076,9 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) #if 1 /* Decrease the audio level for CM119 A/B devices */ if (o->devtype != C108_PRODUCT_ID) { - register short *sp = (short *) (o->usbradio_read_buf + o->readpos); + /* Subtract res from o->readpos in below assignment (o->readpos was incremented + above prior to check of if enough samples were received) */ + register short *sp = (short *) (o->usbradio_read_buf + (o->readpos - res)); register float v; register int i; From 1ce83069c671e64040ea47f34568b2b54bbe1333 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Sun, 6 Oct 2024 17:17:00 -0700 Subject: [PATCH 16/18] Fix issue #399, add legacyaudioscaling parameter to allow scaling/clipping code to remain unchanged for existing installs or if user enables parameter in simpleusb/usbradio .conf. --- channels/chan_simpleusb.c | 25 ++++++++++++++++++++++--- channels/chan_usbradio.c | 12 +++++++++--- configs/rpt/simpleusb.conf | 5 +++++ configs/rpt/usbradio.conf | 5 +++++ configs/samples/simpleusb.conf.sample | 5 +++++ configs/samples/usbradio.conf.sample | 5 +++++ 6 files changed, 51 insertions(+), 6 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 987d3d6..3110b11 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -329,6 +329,8 @@ struct chan_simpleusb_pvt { struct rxaudiostatistics rxaudiostats; + int legacyaudioscaling; + ast_mutex_t usblock; }; @@ -348,7 +350,8 @@ static struct chan_simpleusb_pvt simpleusb_default = { .txoffdelay = 0, .pager = PAGER_NONE, .clipledgpio = 0, - .rxaudiostats.index = 0 + .rxaudiostats.index = 0, + .legacyaudioscaling = 1, }; /* DECLARE FUNCTION PROTOTYPES */ @@ -2112,7 +2115,7 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) */ #if 1 /* Adjust the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { register int v; sp = (short *) o->simpleusb_write_buf; @@ -2404,10 +2407,15 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) } } } + /* Scale the input audio. * o->boost is hardcoded to equal BOOST_SCALE. * This code is not executed. + * TBR: The below should be removed. Raw audio samples should never be clipped or scaled + * for any reason. Adjustments to audio levels should be made only in the USB interface + * mixer settings. */ +#if 0 if (o->boost != BOOST_SCALE) { /* scale and clip values */ register int i, x; register int16_t *p = (int16_t *) f->data.ptr; @@ -2421,8 +2429,14 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) p[i] = x; } } +#endif + /* scale and clip values */ - if (o->rxvoiceadj > 1.0) { + /* TBR: The below should be phased out asap. Raw audio samples should never be clipped + * or scaled for any reason. Adjustments to audio levels should be made only in the + * USB interface mixer settings. */ +#if 1 + if (o->legacyaudioscaling && o->rxvoiceadj > 1.0) { register int i, x; register float f1; register int16_t *p = (int16_t *) f->data.ptr; @@ -2438,6 +2452,8 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) p[i] = x; } } +#endif + /* Compute the peak signal if requested */ if (o->measure_enabled) { register int i; @@ -3092,6 +3108,8 @@ static void _menu_print(int fd, struct chan_simpleusb_pvt *o) ast_cli(fd, "Rx Level currently set to %d\n", o->rxmixerset); ast_cli(fd, "Tx A Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx B Level currently set to %d\n", o->txmixbset); + if(o->legacyaudioscaling) + ast_cli(fd, "legacyaudioscaling is enabled (not recommended)\n"); return; } @@ -3706,6 +3724,7 @@ static struct chan_simpleusb_pvt *store_config(const struct ast_config *cfg, con CV_BOOL("preemphasis", o->preemphasis); CV_UINT("duplex3", o->duplex3); CV_UINT("clipledgpio", o->clipledgpio); + CV_BOOL("legacyaudioscaling", o->legacyaudioscaling); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index f97db4e..c2f9075 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -411,6 +411,8 @@ struct chan_usbradio_pvt { struct rxaudiostatistics rxaudiostats; + int legacyaudioscaling; + ast_mutex_t usblock; }; @@ -431,7 +433,8 @@ static struct chan_usbradio_pvt usbradio_default = { .area = 0, .rptnum = 0, .clipledgpio = 0, - .rxaudiostats.index = 0 + .rxaudiostats.index = 0, + .legacyaudioscaling = 1, }; /* DECLARE FUNCTION PROTOTYPES */ @@ -2075,7 +2078,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) */ #if 1 /* Decrease the audio level for CM119 A/B devices */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { /* Subtract res from o->readpos in below assignment (o->readpos was incremented above prior to check of if enough samples were received) */ register short *sp = (short *) (o->usbradio_read_buf + (o->readpos - res)); @@ -2144,7 +2147,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) */ #if 1 /* For the CM108 adjust the audio level */ - if (o->devtype != C108_PRODUCT_ID) { + if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) o->usbradio_write_buf; register float v; register int i; @@ -3701,6 +3704,8 @@ static void _menu_print(int fd, struct chan_usbradio_pvt *o) ast_cli(fd, "Tx Voice Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx Tone Level currently set to %d\n", o->txctcssadj); ast_cli(fd, "Rx Squelch currently set to %d\n", o->rxsquelchadj); + if(o->legacyaudioscaling) + ast_cli(fd, "legacyaudioscaling is enabled (not recommended)\n"); return; } @@ -4933,6 +4938,7 @@ static struct chan_usbradio_pvt *store_config(const struct ast_config *cfg, cons CV_UINT("txhpf", o->txhpf); CV_UINT("sendvoter", o->sendvoter); CV_UINT("clipledgpio", o->clipledgpio); + CV_BOOL("legacyaudioscaling", o->legacyaudioscaling); CV_END; for (i = 0; i < GPIO_PINCOUNT; i++) { diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index 98e6f49..612cf7d 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -116,6 +116,11 @@ clipledgpio = 1 ; Enable ADC Clip Detect feature to use a GP ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) +legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing + ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. This should be set to no unless you have an + ; existing node with precisely adjusted audio levels but are unable to adjust them. If set to + ; yes, degraded audio quality will result + ;;; End of node-main template ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index f7ccb17..048421d 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -176,6 +176,11 @@ clipledgpio = 0 ; Enable ADC Clip Detect feature to use a GPIO outpu ; Supports URI Clip LED by setting a GPIO (if available) high for 500mS ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) +legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing + ; by 0.83dB and Rx audio levels decreasing by 1.94dB. This should be set to no unless you have an + ; existing node with precisely adjusted audio levels but are not yet able to adjust them. If set to + ; yes, degraded audio quality will result + ;;; End of node-main template ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index e8c7490..022c999 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -106,6 +106,11 @@ ;ppX=out0 ; printer port pin(x) [x is 2-9] initial output state [out0,out1] or 'ptt' (for output pins) ;ppX=in ; printer port pin(x) [x is 10,12,13,15] input pin [in,cor,ctcss] +legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing + ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. This should be set to no unless you have an + ; existing node with precisely adjusted audio levels but are unable to adjust them. If set to + ; yes, degraded audio quality will result + ;[usb] ; First channel unique configuration diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index 9d70587..814e9d0 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -170,6 +170,11 @@ ;ppX=out0 ; printer port pin(x) [x is 2-9] initial output state [out0,out1] or 'ptt' (for output pins) ;ppX=in ; printer port pin(x) [x is 10,12,13,15] input pin [in,cor,ctcss] +legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing + ; by 0.83dB and Rx audio levels decreasing by 1.94dB. This should be set to no unless you have an + ; existing node with precisely adjusted audio levels but are not yet able to adjust them. If set to + ; yes, degraded audio quality will result + ;[usb] ; First channel unique configuration From a476a0b9a090418e6d29059cae02d026dfea4775 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Mon, 7 Oct 2024 13:19:18 -0700 Subject: [PATCH 17/18] Updates from review comments. --- channels/chan_simpleusb.c | 59 ++++++++------------------- channels/chan_usbradio.c | 48 ++++++++-------------- configs/rpt/simpleusb.conf | 6 +-- configs/rpt/usbradio.conf | 6 +-- configs/samples/simpleusb.conf.sample | 6 +-- configs/samples/usbradio.conf.sample | 6 +-- 6 files changed, 46 insertions(+), 85 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 3110b11..599de19 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -181,12 +181,6 @@ struct chan_simpleusb_pvt { #define WARN_speed 2 #define WARN_frag 4 - /* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must - * be representable in 16 bits to avoid overflows. - */ -#define BOOST_SCALE (1<<9) -#define BOOST_MAX 40 /* slightly less than 7 bits */ - int boost; /* input boost, scaled by BOOST_SCALE */ char devicenum; char devstr[128]; int spkrmax; @@ -343,7 +337,6 @@ static struct chan_simpleusb_pvt simpleusb_default = { .queuesize = QUEUE_SIZE, .frags = FRAGS, .readpos = 0, /* start here on reads */ - .boost = BOOST_SCALE, .wanteeprom = 1, .usedtmf = 1, .rxondelay = 0, @@ -351,6 +344,9 @@ static struct chan_simpleusb_pvt simpleusb_default = { .pager = PAGER_NONE, .clipledgpio = 0, .rxaudiostats.index = 0, + /* After the vast majority of existing installs have had a chance to review their + audio settings and the associated old scaling/clipping hacks are no longer in + significant use the following cfg and all related code should be deleted. */ .legacyaudioscaling = 1, }; @@ -2108,12 +2104,14 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) /* enough to fill a frame */ memcpy(o->simpleusb_write_buf + o->simpleusb_write_dst, (char *) f1->data.ptr + src, l); - /* TBR - below is an attempt to match levels to the original CM108 IC which has + /* Below is an attempt to match levels to the original CM108 IC which has * been out of production for over 10 years. Scaling audio to 109.375% will * result in clipping! Any adjustments for CM1xxx gain differences should be * made in the mixer settings, not in the audio stream. + * TODO: After the vast majority of existing installs have had a chance to review their + * audio settings and these old scaling/clipping hacks are no longer in significant use + * the legacyaudioscaling cfg and related code should be deleted. */ -#if 1 /* Adjust the audio level for CM119 A/B devices */ if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { register int v; @@ -2131,7 +2129,6 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) *sp++ = v; } } -#endif sp = (short *) o->simpleusb_write_buf; sp1 = outbuf; @@ -2408,34 +2405,13 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) } } - /* Scale the input audio. - * o->boost is hardcoded to equal BOOST_SCALE. - * This code is not executed. - * TBR: The below should be removed. Raw audio samples should never be clipped or scaled - * for any reason. Adjustments to audio levels should be made only in the USB interface - * mixer settings. + /* Raw audio samples should never be clipped or scaled for any reason. Adjustments to + * audio levels should be made only in the USB interface mixer settings. + * TODO: After the vast majority of existing installs have had a chance to review their + * audio settings and these old scaling/clipping hacks are no longer in significant use + * the legacyaudioscaling cfg and related code should be deleted. */ -#if 0 - if (o->boost != BOOST_SCALE) { /* scale and clip values */ - register int i, x; - register int16_t *p = (int16_t *) f->data.ptr; - for (i = 0; i < f->samples; i++) { - x = (p[i] * o->boost) / BOOST_SCALE; - if (x > 32767) { - x = 32767; - } else if (x < -32768) { - x = -32768; - } - p[i] = x; - } - } -#endif - /* scale and clip values */ - /* TBR: The below should be phased out asap. Raw audio samples should never be clipped - * or scaled for any reason. Adjustments to audio levels should be made only in the - * USB interface mixer settings. */ -#if 1 if (o->legacyaudioscaling && o->rxvoiceadj > 1.0) { register int i, x; register float f1; @@ -2452,7 +2428,6 @@ static struct ast_frame *simpleusb_read(struct ast_channel *c) p[i] = x; } } -#endif /* Compute the peak signal if requested */ if (o->measure_enabled) { @@ -3108,8 +3083,9 @@ static void _menu_print(int fd, struct chan_simpleusb_pvt *o) ast_cli(fd, "Rx Level currently set to %d\n", o->rxmixerset); ast_cli(fd, "Tx A Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx B Level currently set to %d\n", o->txmixbset); - if(o->legacyaudioscaling) - ast_cli(fd, "legacyaudioscaling is enabled (not recommended)\n"); + if(o->legacyaudioscaling) { + ast_cli(fd, "legacyaudioscaling is enabled\n"); + } return; } @@ -3638,7 +3614,8 @@ static void mixer_write(struct chan_simpleusb_pvt *o) ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_PLAYBACK_SW, 0, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_SW_NEW : MIXER_PARAM_SPKR_PLAYBACK_SW, 1, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_VOL_NEW : MIXER_PARAM_SPKR_PLAYBACK_VOL, - ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); /* adjust settings based on the device */ switch (o->devtype) { case C119B_PRODUCT_ID: @@ -3652,7 +3629,7 @@ static void mixer_write(struct chan_simpleusb_pvt *o) /* get interval step size */ f = 1000.0 / (float) o->micmax; } - ast_radio_setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL, mic_setting, 0); + ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_CAPTURE_VOL, mic_setting, 0); ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_BOOST, o->rxboost, 0); ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_CAPTURE_SW, 1, 0); /* set the received voice adjustment factor */ diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index c2f9075..fff9bca 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -194,12 +194,6 @@ struct chan_usbradio_pvt { #define WARN_speed 2 #define WARN_frag 4 - /* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must - * be representable in 16 bits to avoid overflows. - */ -#define BOOST_SCALE (1<<9) -#define BOOST_MAX 40 /* slightly less than 7 bits */ - int boost; /* input boost, scaled by BOOST_SCALE */ char devicenum; char devstr[128]; int spkrmax; @@ -425,7 +419,6 @@ static struct chan_usbradio_pvt usbradio_default = { .queuesize = QUEUE_SIZE, .frags = FRAGS, .readpos = AST_FRIENDLY_OFFSET, /* start here on reads */ - .boost = BOOST_SCALE, .wanteeprom = 1, .usedtmf = 1, .rxondelay = 0, @@ -434,6 +427,9 @@ static struct chan_usbradio_pvt usbradio_default = { .rptnum = 0, .clipledgpio = 0, .rxaudiostats.index = 0, + /* After the vast majority of existing installs have had a chance to review their + audio settings and the associated old scaling/clipping hacks are no longer in + significant use the following cfg and all related code should be deleted. */ .legacyaudioscaling = 1, }; @@ -2070,13 +2066,15 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } } - /* TBR - below is an attempt to match levels to the original CM108 IC which has been + /* Below is an attempt to match levels to the original CM108 IC which has been * out of production for over 10 years. Scaling all rx audio to 80% results in a 20% * loss in dynamic range, added quantization noise, a 2dB reduction in outgoing IAX * audio levels, and inconsistency with Simpleusb. Adjustments for CM1xxx IC gain * differences should be made in the mixer settings, not in the audio stream. + * TODO: After the vast majority of existing installs have had a chance to review their + * audio settings and these old scaling/clipping hacks are no longer in significant use + * the legacyaudioscaling cfg and related code should be deleted. */ -#if 1 /* Decrease the audio level for CM119 A/B devices */ if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { /* Subtract res from o->readpos in below assignment (o->readpos was incremented @@ -2090,7 +2088,6 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } -#endif #if 1 if (o->txkeyed || o->txtestkey || o->echoing) { @@ -2140,12 +2137,14 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } #endif - /* TBR - below is an attempt to match levels to the original CM108 IC which has been + /* Below is an attempt to match levels to the original CM108 IC which has been * out of production for over 10 years. Scaling audio to 110% will result in clipping! * Any adjustments for CM1xxx IC gain differences should be made in the mixer * settings, not in the audio stream. + * TODO: After the vast majority of existing installs have had a chance to review their + * audio settings and these old scaling/clipping hacks are no longer in significant use + * the legacyaudioscaling cfg and related code should be deleted. */ -#if 1 /* For the CM108 adjust the audio level */ if (o->legacyaudioscaling && o->devtype != C108_PRODUCT_ID) { register short *sp = (short *) o->usbradio_write_buf; @@ -2162,7 +2161,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) *sp++ = (int) v; } } -#endif + /* Write the received audio to the sound card */ soundcard_writeframe(o, (short *) o->usbradio_write_buf); @@ -2414,23 +2413,7 @@ static struct ast_frame *usbradio_read(struct ast_channel *c) } } } - /* Scale the input audio. - * o->boost is hardcoded to equal BOOST_SCALE. - * This code is not executed. - */ - if (o->boost != BOOST_SCALE) { /* scale and clip values */ - register int i, x; - register int16_t *p = (int16_t *) f->data.ptr; - for (i = 0; i < f->samples; i++) { - x = (p[i] * o->boost) / BOOST_SCALE; - if (x > 32767) { - x = 32767; - } else if (x < -32768) { - x = -32768; - } - p[i] = x; - } - } + if (o->pmrChan->b.txCtcssReady) { struct ast_frame wf = { AST_FRAME_TEXT }; char msg[32]; @@ -3704,8 +3687,9 @@ static void _menu_print(int fd, struct chan_usbradio_pvt *o) ast_cli(fd, "Tx Voice Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx Tone Level currently set to %d\n", o->txctcssadj); ast_cli(fd, "Rx Squelch currently set to %d\n", o->rxsquelchadj); - if(o->legacyaudioscaling) - ast_cli(fd, "legacyaudioscaling is enabled (not recommended)\n"); + if(o->legacyaudioscaling) { + ast_cli(fd, "legacyaudioscaling is enabled\n"); + } return; } diff --git a/configs/rpt/simpleusb.conf b/configs/rpt/simpleusb.conf index 612cf7d..8954e5a 100644 --- a/configs/rpt/simpleusb.conf +++ b/configs/rpt/simpleusb.conf @@ -117,9 +117,9 @@ clipledgpio = 1 ; Enable ADC Clip Detect feature to use a GP ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing - ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. This should be set to no unless you have an - ; existing node with precisely adjusted audio levels but are unable to adjust them. If set to - ; yes, degraded audio quality will result + ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. Should be set to no unless you have an existing + ; node with precisely adjusted audio levels and are unable to adjust them. This parameter and associated + ; scaling/clipping code will be deleted once existing installs have been able to verify their audio levels ;;; End of node-main template diff --git a/configs/rpt/usbradio.conf b/configs/rpt/usbradio.conf index 048421d..c5609da 100644 --- a/configs/rpt/usbradio.conf +++ b/configs/rpt/usbradio.conf @@ -177,9 +177,9 @@ clipledgpio = 0 ; Enable ADC Clip Detect feature to use a GPIO outpu ; when clipping detected. Value = GPIO# to use (GPIO1 recommended) legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing - ; by 0.83dB and Rx audio levels decreasing by 1.94dB. This should be set to no unless you have an - ; existing node with precisely adjusted audio levels but are not yet able to adjust them. If set to - ; yes, degraded audio quality will result + ; by 0.83dB and Rx audio levels decreasing by 1.94dB. Should be set to no unless you have an existing + ; node with precisely adjusted audio levels and are unable to adjust them. This parameter and associated + ; scaling/clipping code will be deleted once existing installs have been able to verify their audio levels ;;; End of node-main template diff --git a/configs/samples/simpleusb.conf.sample b/configs/samples/simpleusb.conf.sample index 022c999..59e1d4b 100644 --- a/configs/samples/simpleusb.conf.sample +++ b/configs/samples/simpleusb.conf.sample @@ -107,9 +107,9 @@ ;ppX=in ; printer port pin(x) [x is 10,12,13,15] input pin [in,cor,ctcss] legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing - ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. This should be set to no unless you have an - ; existing node with precisely adjusted audio levels but are unable to adjust them. If set to - ; yes, degraded audio quality will result + ; by 0.78dB and Rx audio levels increasing by 0-1.5dB. Should be set to no unless you have an existing + ; node with precisely adjusted audio levels and are unable to adjust them. This parameter and associated + ; scaling/clipping code will be deleted once existing installs have been able to verify their audio levels ;[usb] ; First channel unique configuration diff --git a/configs/samples/usbradio.conf.sample b/configs/samples/usbradio.conf.sample index 814e9d0..7961066 100644 --- a/configs/samples/usbradio.conf.sample +++ b/configs/samples/usbradio.conf.sample @@ -171,9 +171,9 @@ ;ppX=in ; printer port pin(x) [x is 10,12,13,15] input pin [in,cor,ctcss] legacyaudioscaling = no ; If yes, continue to do raw audio sample scaling and clipping, resulting in Tx audio levels increasing - ; by 0.83dB and Rx audio levels decreasing by 1.94dB. This should be set to no unless you have an - ; existing node with precisely adjusted audio levels but are not yet able to adjust them. If set to - ; yes, degraded audio quality will result + ; by 0.83dB and Rx audio levels decreasing by 1.94dB. Should be set to no unless you have an existing + ; node with precisely adjusted audio levels and are unable to adjust them. This parameter and associated + ; scaling/clipping code will be deleted once existing installs have been able to verify their audio levels ;[usb] ; First channel unique configuration From 889be418ad59bbd4bd254539c2ceb302c1a05172 Mon Sep 17 00:00:00 2001 From: David Gleason Date: Mon, 7 Oct 2024 15:01:01 -0700 Subject: [PATCH 18/18] whitespace cleanups --- channels/chan_simpleusb.c | 6 +++--- channels/chan_usbradio.c | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/channels/chan_simpleusb.c b/channels/chan_simpleusb.c index 599de19..4b11e16 100644 --- a/channels/chan_simpleusb.c +++ b/channels/chan_simpleusb.c @@ -3083,7 +3083,7 @@ static void _menu_print(int fd, struct chan_simpleusb_pvt *o) ast_cli(fd, "Rx Level currently set to %d\n", o->rxmixerset); ast_cli(fd, "Tx A Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx B Level currently set to %d\n", o->txmixbset); - if(o->legacyaudioscaling) { + if (o->legacyaudioscaling) { ast_cli(fd, "legacyaudioscaling is enabled\n"); } return; @@ -3614,8 +3614,8 @@ static void mixer_write(struct chan_simpleusb_pvt *o) ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_PLAYBACK_SW, 0, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_SW_NEW : MIXER_PARAM_SPKR_PLAYBACK_SW, 1, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_VOL_NEW : MIXER_PARAM_SPKR_PLAYBACK_VOL, - ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), - ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); /* adjust settings based on the device */ switch (o->devtype) { case C119B_PRODUCT_ID: diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c index fff9bca..dcac0e9 100644 --- a/channels/chan_usbradio.c +++ b/channels/chan_usbradio.c @@ -3687,7 +3687,7 @@ static void _menu_print(int fd, struct chan_usbradio_pvt *o) ast_cli(fd, "Tx Voice Level currently set to %d\n", o->txmixaset); ast_cli(fd, "Tx Tone Level currently set to %d\n", o->txctcssadj); ast_cli(fd, "Rx Squelch currently set to %d\n", o->rxsquelchadj); - if(o->legacyaudioscaling) { + if (o->legacyaudioscaling) { ast_cli(fd, "legacyaudioscaling is enabled\n"); } return; @@ -4506,7 +4506,8 @@ static void mixer_write(struct chan_usbradio_pvt *o) ast_radio_setamixer(o->devicenum, MIXER_PARAM_MIC_PLAYBACK_SW, 0, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_SW_NEW : MIXER_PARAM_SPKR_PLAYBACK_SW, 1, 0); ast_radio_setamixer(o->devicenum, (o->newname) ? MIXER_PARAM_SPKR_PLAYBACK_VOL_NEW : MIXER_PARAM_SPKR_PLAYBACK_VOL, - ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixaset, o->devtype), + ast_radio_make_spkr_playback_value(o->spkrmax, o->txmixbset, o->devtype)); /* adjust settings based on the device */ switch (o->devtype) { case C119B_PRODUCT_ID: