Skip to content

Commit

Permalink
- add command-line option to sinusoidally cycle through ratio range
Browse files Browse the repository at this point in the history
- update to version 0.2 (README and main.c)
  • Loading branch information
dbry committed Nov 28, 2019
1 parent e6d1e3c commit f151c6f
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
12 changes: 11 additions & 1 deletion README
Expand Up @@ -40,20 +40,29 @@ properly play these files, and audio editing programs will likely import
them correctly (by resampling), but it is possible that some applications
will barf on them.

For version 0.2 a new option was added to cycle through the full possible
ratio range in a sinusoidal pattern, starting at 1.0, and either going
up (-c) or down (-cc) first. In this case any specified ratio is ignored
(except if the -s option is also specified to scale the sampling rate).
The total period is fixed at 2π seconds, at which point the output will
again be exactly aligned with the input.

To build the demo app:

$ gcc -O2 *.c -o audio-stretch

The "help" display from the demo app:

AUDIO-STRETCH Time Domain Harmonic Scaling Demo Version 0.1
AUDIO-STRETCH Time Domain Harmonic Scaling Demo Version 0.2
Copyright (c) 2019 David Bryant. All Rights Reserved.

Usage: AUDIO-STRETCH [-options] infile.wav outfile.wav

Options: -r<n.n> = stretch ratio (0.5 to 2.0, default = 1.0)
-u<n> = upper freq period limit (default = 333 Hz)
-l<n> = lower freq period limit (default = 55 Hz)
-c = cycle through all ratios, starting higher
-cc = cycle through all ratios, starting lower
-s = scale rate to preserve duration (not pitch)
-f = fast pitch detection (default >= 32 kHz)
-n = normal pitch detection (default < 32 kHz)
Expand All @@ -68,6 +77,7 @@ Notes:
1. The program will handle only mono or stereo files in the WAV format. The
audio must be 16-bit PCM and the acceptable sampling rates are from 8,000
to 48,000 Hz. Any additional RIFF info in the WAV file will be discarded.
The command-line program is only for little-endian architectures.

2. For stereo files, the pitch detection is done on a mono conversion of the
audio, but the scaling transformation is done on the independent channels.
Expand Down
25 changes: 19 additions & 6 deletions main.c
Expand Up @@ -14,18 +14,21 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <math.h>

#include "stretch.h"

static const char *sign_on = "\n"
" AUDIO-STRETCH Time Domain Harmonic Scaling Demo Version 0.1\n"
" AUDIO-STRETCH Time Domain Harmonic Scaling Demo Version 0.2\n"
" Copyright (c) 2019 David Bryant. All Rights Reserved.\n\n";

static const char *usage =
" Usage: AUDIO-STRETCH [-options] infile.wav outfile.wav\n\n"
" Options: -r<n.n> = stretch ratio (0.5 to 2.0, default = 1.0)\n"
" -u<n> = upper freq period limit (default = 333 Hz)\n"
" -l<n> = lower freq period limit (default = 55 Hz)\n"
" -c = cycle through all ratios, starting higher\n"
" -cc = cycle through all ratios, starting lower\n"
" -s = scale rate to preserve duration (not pitch)\n"
" -f = fast pitch detection (default >= 32 kHz)\n"
" -n = normal pitch detection (default < 32 kHz)\n"
Expand Down Expand Up @@ -71,7 +74,7 @@ static int verbose_mode, quiet_mode;

int main (argc, argv) int argc; char **argv;
{
int asked_help = 0, overwrite = 0, scale_rate = 0, force_fast = 0, force_normal = 0, fast_mode, scaled_rate;
int asked_help = 0, overwrite = 0, scale_rate = 0, force_fast = 0, force_normal = 0, cycle_ratio = 0;
int upper_frequency = 333, lower_frequency = 55, min_period, max_period;
int samples_to_process, insamples = 0, outsamples = 0;
char *infilename = NULL, *outfilename = NULL;
Expand Down Expand Up @@ -130,6 +133,10 @@ int main (argc, argv) int argc; char **argv;
scale_rate = 1;
break;

case 'C': case 'c':
cycle_ratio++;
break;

case 'F': case 'f':
force_fast = 1;
break;
Expand Down Expand Up @@ -301,15 +308,18 @@ int main (argc, argv) int argc; char **argv;

min_period = WaveHeader.SampleRate / upper_frequency;
max_period = WaveHeader.SampleRate / lower_frequency;
fast_mode = (force_fast || WaveHeader.SampleRate >= 32000) && !force_normal;
int fast_mode = (force_fast || WaveHeader.SampleRate >= 32000) && !force_normal;

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");

if (!quiet_mode && ratio == 1.0)
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");

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);

if (!stretcher) {
Expand All @@ -324,11 +334,11 @@ int main (argc, argv) int argc; char **argv;
return 1;
}

scaled_rate = scale_rate ? (int)(WaveHeader.SampleRate * ratio + 0.5) : WaveHeader.SampleRate;
int scaled_rate = scale_rate ? (int)(WaveHeader.SampleRate * ratio + 0.5) : WaveHeader.SampleRate;
write_pcm_wav_header (outfile, 0, WaveHeader.NumChannels, 2, scaled_rate);

short *inbuffer = malloc (BUFFER_SAMPLES * WaveHeader.BlockAlign);
short *outbuffer = malloc ((BUFFER_SAMPLES * 2 + 2048) * WaveHeader.BlockAlign);
short *outbuffer = malloc ((BUFFER_SAMPLES * 2 + max_period * 3) * WaveHeader.BlockAlign);

while (1) {
int samples_read = fread (inbuffer, WaveHeader.BlockAlign,
Expand All @@ -338,6 +348,9 @@ int main (argc, argv) int argc; char **argv;
insamples += samples_read;
samples_to_process -= samples_read;

if (cycle_ratio)
ratio = (sin ((double) outsamples / WaveHeader.SampleRate) * (cycle_ratio & 1 ? 0.75 : -0.75)) + 1.25;

if (samples_read)
samples_generated = stretch_samples (stretcher, inbuffer, samples_read, outbuffer, ratio);
else
Expand Down

0 comments on commit f151c6f

Please sign in to comment.