ref: b56508b18124746746909f74dfad5b626580f55f
parent: 068d11f041e60f9dc4b13fe72c114fb5a5f407b5
author: David Bryant <david@wavpack.com>
date: Wed Oct 12 12:37:34 EDT 2022
issue #5: experimental version incorporating cascaded instances for extended stretch range
--- a/main.c
+++ b/main.c
@@ -121,8 +121,8 @@
case 'R': case 'r':
ratio = strtod (++*argv, argv);
- if (ratio < 0.5 || ratio > 2.0) {
- fprintf (stderr, "\nratio must be from 0.5 to 2.0!\n");
+ if (ratio < 0.25 || ratio > 4.0) {
+ fprintf (stderr, "\nratio must be from 0.25 to 4.0!\n");
return -1;
}
@@ -308,11 +308,17 @@
min_period = WaveHeader.SampleRate / upper_frequency;
max_period = WaveHeader.SampleRate / lower_frequency;
- int fast_mode = (force_fast || WaveHeader.SampleRate >= 32000) && !force_normal;
+ int flags = 0;
+ if (ratio < 0.5 || ratio > 2.0)
+ flags |= STRETCH_DUAL_FLAG;
+
+ if ((force_fast || WaveHeader.SampleRate >= 32000) && !force_normal)
+ flags |= STRETCH_FAST_FLAG;
+
if (verbose_mode)
fprintf (stderr, "initializing stretch library with period range = %d to %d, %d channels, %s\n",
- min_period, max_period, WaveHeader.NumChannels, fast_mode ? "fast mode" : "normal mode");
+ min_period, max_period, WaveHeader.NumChannels, (flags & STRETCH_FAST_FLAG) ? "fast mode" : "normal mode");
if (!quiet_mode && ratio == 1.0 && !cycle_ratio)
fprintf (stderr, "warning: a ratio of 1.0 will do nothing but copy the WAV file!\n");
@@ -320,7 +326,7 @@
if (!quiet_mode && ratio != 1.0 && cycle_ratio && !scale_rate)
fprintf (stderr, "warning: specifying ratio with cycling doesn't do anything (unless scaling rate)\n");
- stretcher = stretch_init (min_period, max_period, WaveHeader.NumChannels, fast_mode);
+ stretcher = stretch_init (min_period, max_period, WaveHeader.NumChannels, flags);
if (!stretcher) {
fprintf (stderr, "can't initialize stretcher\n");
@@ -338,7 +344,7 @@
write_pcm_wav_header (outfile, 0, WaveHeader.NumChannels, 2, scaled_rate);
int16_t *inbuffer = malloc (BUFFER_SAMPLES * WaveHeader.BlockAlign);
- int16_t *outbuffer = malloc ((BUFFER_SAMPLES * 2 + max_period * 4) * WaveHeader.BlockAlign);
+ int16_t *outbuffer = malloc ((BUFFER_SAMPLES * 4 + max_period * 8) * WaveHeader.BlockAlign);
if (!inbuffer || !outbuffer) {
fprintf (stderr, "can't allocate required memory!\n");
--- a/stretch.c
+++ b/stretch.c
@@ -49,6 +49,9 @@
int16_t *inbuff, *calcbuff;
float outsamples_error;
uint32_t *results;
+
+ struct stretch_cnxt *next;
+ int16_t *intermediate;
};
static void merge_blocks (int16_t *output, int16_t *input1, int16_t *input2, int samples);
@@ -63,11 +66,11 @@
* worst-case performance will suffer if too short a period is selected.
*/
-StretchHandle stretch_init (int shortest_period, int longest_period, int num_channels, int fast_mode)
+StretchHandle stretch_init (int shortest_period, int longest_period, int num_channels, int flags)
{
struct stretch_cnxt *cnxt;
- if (fast_mode) {
+ if (flags & STRETCH_FAST_FLAG) {
longest_period = (longest_period + 1) & ~1;
shortest_period &= ~1;
}
@@ -83,23 +86,28 @@
cnxt->inbuff_samples = longest_period * num_channels * 6;
cnxt->inbuff = calloc (cnxt->inbuff_samples, sizeof (*cnxt->inbuff));
- if (num_channels == 2 || fast_mode)
+ if (num_channels == 2 || (flags & STRETCH_FAST_FLAG))
cnxt->calcbuff = calloc (longest_period * num_channels, sizeof (*cnxt->calcbuff));
- if (fast_mode)
+ if ((flags & STRETCH_FAST_FLAG))
cnxt->results = calloc (longest_period, sizeof (*cnxt->results));
}
- if (!cnxt || !cnxt->inbuff || (num_channels == 2 && fast_mode && !cnxt->calcbuff) || (fast_mode && !cnxt->results)) {
+ if (!cnxt || !cnxt->inbuff || (num_channels == 2 && (flags & STRETCH_FAST_FLAG) && !cnxt->calcbuff) || ((flags & STRETCH_FAST_FLAG) && !cnxt->results)) {
fprintf (stderr, "stretch_init(): out of memory!\n");
return NULL;
}
cnxt->head = cnxt->tail = cnxt->longest = longest_period * num_channels;
+ cnxt->fast_mode = (flags & STRETCH_FAST_FLAG) ? 1 : 0;
cnxt->shortest = shortest_period * num_channels;
cnxt->num_chans = num_channels;
- cnxt->fast_mode = fast_mode;
+ if (flags & STRETCH_DUAL_FLAG) {
+ cnxt->next = stretch_init (shortest_period, longest_period, num_channels, flags & ~STRETCH_DUAL_FLAG);
+ cnxt->intermediate = calloc (longest_period * num_channels * 4, sizeof (*cnxt->intermediate));
+ }
+
return (StretchHandle) cnxt;
}
@@ -110,8 +118,11 @@
void stretch_reset (StretchHandle handle)
{
- struct stretch_cnxt *cnxt = (struct stretch_cnxt *) handle;
- cnxt->head = cnxt->tail = cnxt->longest;
+ struct stretch_cnxt *cnxt = (struct stretch_cnxt *) handle;
+ cnxt->head = cnxt->tail = cnxt->longest;
+
+ if (cnxt->next)
+ cnxt->next->head = cnxt->next->tail = cnxt->next->longest;
}
@@ -127,8 +138,14 @@
int stretch_samples (StretchHandle handle, const int16_t *samples, int num_samples, int16_t *output, float ratio)
{
struct stretch_cnxt *cnxt = (struct stretch_cnxt *) handle;
- int out_samples = 0;
+ int out_samples = 0, next_samples = 0;
+ int16_t *outbuf = output;
+ if (cnxt->next) {
+ outbuf = cnxt->intermediate;
+ ratio = sqrt (ratio);
+ }
+
num_samples *= cnxt->num_chans;
if (ratio < 0.5)
@@ -177,7 +194,7 @@
process_ratio = ceil (ratio * 2.0) / 2.0;
if (process_ratio == 0.5) {
- merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
+ merge_blocks (outbuf + out_samples, cnxt->inbuff + cnxt->tail,
cnxt->inbuff + cnxt->tail + period, period);
cnxt->outsamples_error += period - (period * 2.0 * ratio);
out_samples += period;
@@ -184,22 +201,22 @@
cnxt->tail += period * 2;
}
else if (process_ratio == 1.0) {
- memcpy (output + out_samples, cnxt->inbuff + cnxt->tail, period * 2 * sizeof (cnxt->inbuff [0]));
+ memcpy (outbuf + 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,
+ memcpy (outbuf + out_samples, cnxt->inbuff + cnxt->tail, period * sizeof (cnxt->inbuff [0]));
+ merge_blocks (outbuf + 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]));
+ memcpy (outbuf + 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,
+ merge_blocks (outbuf + out_samples, cnxt->inbuff + cnxt->tail,
cnxt->inbuff + cnxt->tail - period, period * 2);
cnxt->outsamples_error += (period * 2.0) - (period * ratio);
@@ -207,7 +224,7 @@
cnxt->tail += period;
if (cnxt->fast_mode) {
- merge_blocks (output + out_samples, cnxt->inbuff + cnxt->tail,
+ merge_blocks (outbuf + out_samples, cnxt->inbuff + cnxt->tail,
cnxt->inbuff + cnxt->tail - period, period * 2);
cnxt->outsamples_error += (period * 2.0) - (period * ratio);
@@ -217,6 +234,11 @@
}
else
fprintf (stderr, "stretch_samples: fatal programming error: process_ratio == %g\n", process_ratio);
+
+ if (cnxt->next) {
+ next_samples += stretch_samples (cnxt->next, outbuf, out_samples / cnxt->num_chans, output + next_samples * cnxt->num_chans, ratio);
+ out_samples = 0;
+ }
}
/* if we're almost done with buffer, copy the rest back to beginning */
@@ -233,7 +255,7 @@
} while (num_samples);
- return out_samples / cnxt->num_chans;
+ return cnxt->next ? next_samples : out_samples / cnxt->num_chans;
}
/* flush any leftover samples out at normal speed */
@@ -258,6 +280,10 @@
free (cnxt->calcbuff);
free (cnxt->results);
free (cnxt->inbuff);
+
+ if (cnxt->next)
+ stretch_deinit (cnxt->next);
+
free (cnxt);
}
--- a/stretch.h
+++ b/stretch.h
@@ -27,6 +27,9 @@
#include <stdint.h>
+#define STRETCH_FAST_FLAG 0x1
+#define STRETCH_DUAL_FLAG 0x2
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -33,7 +36,7 @@
typedef void *StretchHandle;
-StretchHandle stretch_init (int shortest_period, int longest_period, int num_chans, int fast_mode);
+StretchHandle stretch_init (int shortest_period, int longest_period, int num_chans, int flags);
int stretch_samples (StretchHandle handle, const int16_t *samples, int num_samples, int16_t *output, float ratio);
int stretch_flush (StretchHandle handle, int16_t *output);
void stretch_reset (StretchHandle handle);