ref: 7963f09aeefb9b584f7a415b11e8e08310ed04c3
parent: 52245b283aae83e894712ed0ed85988db5536aff
author: David Bryant <david@wavpack.com>
date: Thu Sep 29 15:09:05 EDT 2022
Fix overflow possibility triggered by pathological (e.g., clipping) audio - replace fixed integer scale factor with dynamic one based on sample sum - results in higher precision with typical audio and typical max periods - allows increasing the maximum period to 2400 samples (20 Hz at 48 kHz) - thanks hayguen for finding overflow situation and suggested fixes! - fix annoying warning about strncpy()
--- a/main.c
+++ b/main.c
@@ -402,13 +402,13 @@
wavhdr.BlockAlign = bytes_per_sample * num_channels;
wavhdr.BitsPerSample = bytes_per_sample * 8;
- strncpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID));
- strncpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType));
+ memcpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID));
+ memcpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType));
riffhdr.ckSize = sizeof (riffhdr) + wavhdrsize + sizeof (datahdr) + total_data_bytes;
- strncpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID));
+ memcpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID));
fmthdr.ckSize = wavhdrsize;
- strncpy (datahdr.ckID, "data", sizeof (datahdr.ckID));
+ memcpy (datahdr.ckID, "data", sizeof (datahdr.ckID));
datahdr.ckSize = total_data_bytes;
return fwrite (&riffhdr, sizeof (riffhdr), 1, outfile) &&
--- a/stretch.c
+++ b/stretch.c
@@ -24,7 +24,7 @@
#include "stretch.h"
#define MIN_PERIOD 24 /* minimum allowable pitch period */
-#define MAX_PERIOD 1024 /* maximum allowable pitch period */
+#define MAX_PERIOD 2400 /* maximum allowable pitch period */
struct stretch_cnxt {
int num_chans, inbuff_samples, shortest, longest, tail, head, fast_mode;
@@ -247,7 +247,7 @@
static int find_period (struct stretch_cnxt *cnxt, short *samples)
{
- unsigned int sum, diff, factor, best_factor = 0;
+ unsigned int sum, diff, factor, scaler, best_factor = 0;
short *calcbuff = samples;
int period, best_period;
int i, j;
@@ -254,13 +254,25 @@
period = best_period = cnxt->shortest / cnxt->num_chans;
+ // convert stereo to mono, and accumulate sum for longest period
+
if (cnxt->num_chans == 2) {
calcbuff = cnxt->calcbuff;
- for (i = j = 0; i < cnxt->longest * 2; i += 2)
- calcbuff [j++] = (samples [i] + samples [i+1]) >> 1;
+ for (sum = i = j = 0; i < cnxt->longest * 2; i += 2)
+ sum += abs (calcbuff [j++] = (samples [i] + samples [i+1]) >> 1);
}
+ else
+ for (sum = i = 0; i < cnxt->longest; ++i)
+ sum += abs (calcbuff [i]) + abs (calcbuff [i+cnxt->longest]);
+ // if silence return longest period, else calculate scaler based on largest sum
+
+ if (sum)
+ scaler = (UINT32_MAX - 1) / sum;
+ else
+ return cnxt->longest;
+
/* accumulate sum for shortest period size */
for (sum = i = 0; i < period; ++i)
@@ -283,11 +295,10 @@
* Here we calculate and store the resulting correlation
* factor. Note that we must watch for a difference of
* zero, meaning a perfect match. Also, for increased
- * precision using integer math, we scale the sum. Care
- * must be taken here to avoid possibility of overflow.
+ * precision using integer math, we scale the sum.
*/
- factor = diff ? (sum * 128) / diff : UINT32_MAX;
+ factor = diff ? (sum * scaler) / diff : UINT32_MAX;
if (factor >= best_factor) {
best_factor = factor;
@@ -302,7 +313,8 @@
/* update accumulating sum and current period */
sum += abs (calcbuff [period * 2]);
- sum += abs (calcbuff [period++ * 2 + 1]);
+ sum += abs (calcbuff [period * 2 + 1]);
+ period++;
}
return best_period * cnxt->num_chans;
@@ -320,21 +332,28 @@
static int find_period_fast (struct stretch_cnxt *cnxt, short *samples)
{
- unsigned int sum, diff, best_factor = 0;
+ unsigned int sum, diff, scaler, best_factor = 0;
int period, best_period;
int i, j;
best_period = period = cnxt->shortest / (cnxt->num_chans * 2);
- /* first step is compressing data 2:1 into calcbuff */
+ /* first step is compressing data 2:1 into calcbuff, and calculating maximum sum */
if (cnxt->num_chans == 2)
- for (i = j = 0; i < cnxt->longest * 2; i += 4)
- cnxt->calcbuff [j++] = (samples [i] + samples [i+1] + samples [i+2] + samples [i+3]) >> 2;
+ for (sum = i = j = 0; i < cnxt->longest * 2; i += 4)
+ sum += abs (cnxt->calcbuff [j++] = (samples [i] + samples [i+1] + samples [i+2] + samples [i+3]) >> 2);
else
- for (i = j = 0; i < cnxt->longest * 2; i += 2)
- cnxt->calcbuff [j++] = (samples [i] + samples [i+1]) >> 1;
+ for (sum = i = j = 0; i < cnxt->longest * 2; i += 2)
+ sum += abs (cnxt->calcbuff [j++] = (samples [i] + samples [i+1]) >> 1);
+ // if silence return longest period, else calculate scaler based on largest sum
+
+ if (sum)
+ scaler = (UINT32_MAX - 1) / sum;
+ else
+ return cnxt->longest;
+
/* accumulate sum for shortest period */
for (sum = i = 0; i < period; ++i)
@@ -357,11 +376,10 @@
* Here we calculate and store the resulting correlation
* factor. Note that we must watch for a difference of
* zero, meaning a perfect match. Also, for increased
- * precision using integer math, we scale the sum. Care
- * must be taken here to avoid possibility of overflow.
+ * precision using integer math, we scale the sum.
*/
- cnxt->results [period] = diff ? (sum * 128) / diff : UINT32_MAX;
+ cnxt->results [period] = diff ? (sum * scaler) / diff : UINT32_MAX;
if (cnxt->results [period] >= best_factor) { /* check if best yet */
best_factor = cnxt->results [period];
@@ -376,7 +394,8 @@
/* update accumulating sum and current period */
sum += abs (cnxt->calcbuff [period * 2]);
- sum += abs (cnxt->calcbuff [period++ * 2 + 1]);
+ sum += abs (cnxt->calcbuff [period * 2 + 1]);
+ period++;
}
if (best_period * cnxt->num_chans * 2 != cnxt->shortest && best_period * cnxt->num_chans * 2 != cnxt->longest) {