ref: e6d1e3cad1eb9ef8f59afb597f5dddddb5bf86aa
parent: d9d2b5e927bd607d98b2b76d575f3ff1cd33531a
author: David Bryant <david@wavpack.com>
date: Wed Nov 27 11:26:15 EST 2019
- improve quality for positive ratios with new 2:3 scaling method - improve accuracy of scaling ratio with new "error" accumulator
--- a/stretch.c
+++ b/stretch.c
@@ -27,8 +27,9 @@
#define MAX_PERIOD 1024 /* maximum allowable pitch period */
struct stretch_cnxt {
- int num_chans, inbuff_samples, shortest, longest, tail, head, verbatim, fast_mode;
+ int num_chans, inbuff_samples, shortest, longest, tail, head, fast_mode;
short *inbuff, *calcbuff;
+ float outsamples_error;
unsigned int *results;
};
@@ -54,7 +55,7 @@
}
if (longest_period <= shortest_period || shortest_period < MIN_PERIOD || longest_period > MAX_PERIOD) {
- printf ("stretch_init(): invalid periods!\n");
+ fprintf (stderr, "stretch_init(): invalid periods!\n");
return NULL;
}
@@ -86,7 +87,7 @@
* both channels in stereo and can be as large as desired (samples are buffered
* here). The exact number of samples output is not possible to determine in
* advance, but it will be close to the number of input samples times the ratio
- * plus or minus a couple of the longest periods.
+ * plus or minus 3X the longest period.
*/
int stretch_samples (StretchHandle handle, short *samples, int num_samples, short *output, float ratio)
@@ -120,54 +121,59 @@
/* while there are enough samples to process, do so */
- while (1) {
- if (cnxt->verbatim && cnxt->head > cnxt->tail) {
- int samples_to_copy = cnxt->verbatim;
- if (samples_to_copy > cnxt->head - cnxt->tail)
- samples_to_copy = cnxt->head - cnxt->tail;
+ while (cnxt->tail >= cnxt->longest && cnxt->head - cnxt->tail >= cnxt->longest * 2) {
+ int period = cnxt->fast_mode ? find_period_fast (cnxt, cnxt->inbuff + cnxt->tail) :
+ find_period (cnxt, cnxt->inbuff + cnxt->tail);
+ float process_ratio;
- memcpy (output + out_samples, cnxt->inbuff + cnxt->tail, samples_to_copy * sizeof (cnxt->inbuff [0]));
- out_samples += samples_to_copy;
- cnxt->verbatim -= samples_to_copy;
- cnxt->tail += samples_to_copy;
- }
- else if (cnxt->tail >= cnxt->longest && cnxt->head - cnxt->tail >= cnxt->longest * 2) {
- if (ratio == 1.0) {
- memcpy (output + out_samples, cnxt->inbuff + cnxt->tail,
- cnxt->longest * 2 * sizeof (cnxt->inbuff [0]));
+ /*
+ * Once we have calculated the best-match period, there are 4 possible transformations
+ * available to convert the input samples to output samples. Obviously we can simply
+ * copy the samples verbatim (1:1). Standard TDHS provides algorithms for 2:1 and
+ * 1:2 scaling, and I have created an obvious extension for 2:3 scaling. To achieve
+ * intermediate ratios we maintain a "error" term (in samples) and use that here to
+ * calculate the actual transformation to apply.
+ */
- out_samples += cnxt->longest * 2;
- cnxt->tail += cnxt->longest * 2;
- }
- else {
- int period = cnxt->fast_mode ? find_period_fast (cnxt, cnxt->inbuff + cnxt->tail) :
- find_period (cnxt, cnxt->inbuff + cnxt->tail), incnt, outcnt;
+ if (cnxt->outsamples_error == 0.0)
+ process_ratio = floor (ratio * 2.0 + 0.5) / 2.0;
+ else if (cnxt->outsamples_error > 0.0)
+ process_ratio = floor (ratio * 2.0) / 2.0;
+ else
+ process_ratio = ceil (ratio * 2.0) / 2.0;
- if (ratio < 1.0) {
- merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
- cnxt->inbuff + cnxt->tail + period, period);
+ if (process_ratio == 0.5) {
+ merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
+ cnxt->inbuff + cnxt->tail + period, period);
+ cnxt->outsamples_error += period - (period * 2.0 * ratio);
+ out_samples += period;
+ cnxt->tail += period * 2;
+ }
+ else if (process_ratio == 1.0) {
+ memcpy (output + out_samples, cnxt->inbuff + cnxt->tail, period * 2 * sizeof (cnxt->inbuff [0]));
+ cnxt->outsamples_error += (period * 2.0) - (period * 2.0 * ratio);
+ out_samples += period * 2;
+ cnxt->tail += period * 2;
+ }
+ else if (process_ratio == 1.5) {
+ memcpy (output + out_samples, cnxt->inbuff + cnxt->tail, period * sizeof (cnxt->inbuff [0]));
+ merge_blocks (output + out_samples + period, cnxt->inbuff + cnxt->tail + period,
+ cnxt->inbuff + cnxt->tail, period);
+ memcpy (output + out_samples + period * 2, cnxt->inbuff + cnxt->tail + period, period * sizeof (cnxt->inbuff [0]));
+ cnxt->outsamples_error += (period * 3.0) - (period * 2.0 * ratio);
+ out_samples += period * 3;
+ cnxt->tail += period * 2;
+ }
+ else if (process_ratio == 2.0) {
+ merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
+ cnxt->inbuff + cnxt->tail - period, period * 2);
- out_samples += period;
- cnxt->tail += period * 2;
- incnt = (outcnt = period) * 2;
- }
- else {
- merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
- cnxt->inbuff + cnxt->tail - period, period * 2);
-
- out_samples += period * 2;
- cnxt->tail += period;
- outcnt = (incnt = period) * 2;
- }
-
- cnxt->verbatim = (int) floor (((ratio * incnt) - outcnt) / (1.0 - ratio) / cnxt->num_chans + 0.5) * cnxt->num_chans;
-
- if (cnxt->verbatim < 0) // this should not happen...
- cnxt->verbatim = 0;
- }
+ cnxt->outsamples_error += (period * 2.0) - (period * ratio);
+ out_samples += period * 2;
+ cnxt->tail += period;
}
else
- break;
+ fprintf (stderr, "stretch_samples: fatal programming error: process_ratio == %g\n", process_ratio);
}
/* if we're almost done with buffer, copy the rest back to beginning */