blob: cc6371df6099b03358a0040d245929ce5d7af3a0 [file] [log] [blame]
/* This file was written by Bill Cox in 2010, and is licensed under the Apache
2.0 license.
This file is meant as a simple example for how to use libsonic. It is also a
useful utility on its own, which can speed up or slow down wav files, change
pitch, and scale volume. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sonic.h"
#include "wave.h"
#define BUFFER_SIZE 2048
/* Run sonic. */
static void runSonic(char* inFileName, char* outFileName, float speed,
float pitch, float rate, float volume, int outputSampleRate,
int emulateChordPitch, int quality, int computeSpectrogram,
int numRows, int numCols) {
waveFile inFile, outFile = NULL;
sonicStream stream;
short inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE];
int sampleRate, numChannels, samplesRead, samplesWritten;
inFile = openInputWaveFile(inFileName, &sampleRate, &numChannels);
if (outputSampleRate != 0) {
sampleRate = outputSampleRate;
}
if (inFile == NULL) {
fprintf(stderr, "Unable to read wave file %s\n", inFileName);
exit(1);
}
if (!computeSpectrogram) {
outFile = openOutputWaveFile(outFileName, sampleRate, numChannels);
if (outFile == NULL) {
closeWaveFile(inFile);
fprintf(stderr, "Unable to open wave file %s for writing\n", outFileName);
exit(1);
}
}
stream = sonicCreateStream(sampleRate, numChannels);
sonicSetSpeed(stream, speed);
sonicSetPitch(stream, pitch);
sonicSetRate(stream, rate);
sonicSetVolume(stream, volume);
sonicSetChordPitch(stream, emulateChordPitch);
sonicSetQuality(stream, quality);
#ifdef SONIC_SPECTROGRAM
if (computeSpectrogram) {
sonicComputeSpectrogram(stream);
}
#endif /* SONIC_SPECTROGRAM */
do {
samplesRead = readFromWaveFile(inFile, inBuffer, BUFFER_SIZE / numChannels);
if (samplesRead == 0) {
sonicFlushStream(stream);
} else {
sonicWriteShortToStream(stream, inBuffer, samplesRead);
}
if (!computeSpectrogram) {
do {
samplesWritten = sonicReadShortFromStream(stream, outBuffer,
BUFFER_SIZE / numChannels);
if (samplesWritten > 0 && !computeSpectrogram) {
writeToWaveFile(outFile, outBuffer, samplesWritten);
}
} while (samplesWritten > 0);
}
} while (samplesRead > 0);
#ifdef SONIC_SPECTROGRAM
if (computeSpectrogram) {
sonicSpectrogram spectrogram = sonicGetSpectrogram(stream);
sonicBitmap bitmap =
sonicConvertSpectrogramToBitmap(spectrogram, numRows, numCols);
sonicWritePGM(bitmap, outFileName);
sonicDestroyBitmap(bitmap);
}
#endif /* SONIC_SPECTROGRAM */
sonicDestroyStream(stream);
closeWaveFile(inFile);
if (!computeSpectrogram) {
closeWaveFile(outFile);
}
}
/* Print the usage. */
static void usage(void) {
fprintf(
stderr,
"Usage: sonic [OPTION]... infile outfile\n"
" -c -- Modify pitch by emulating vocal chords vibrating\n"
" faster or slower.\n"
" -o -- Override the sample rate of the output. -o 44200\n"
" on an input file at 22100 KHz will play twice as fast\n"
" and have twice the pitch.\n"
" -p pitch -- Set pitch scaling factor. 1.3 means 30%% higher.\n"
" -q -- Disable speed-up heuristics. May increase quality.\n"
" -r rate -- Set playback rate. 2.0 means 2X faster, and 2X "
"pitch.\n"
" -s speed -- Set speed up factor. 2.0 means 2X faster.\n"
#ifdef SONIC_SPECTROGRAM
" -S width height -- Write a spectrogram in outfile in PGM format.\n"
#endif /* SONIC_SPECTROGRAM */
" -v volume -- Scale volume by a constant factor.\n");
exit(1);
}
int main(int argc, char** argv) {
char* inFileName;
char* outFileName;
float speed = 1.0f;
float pitch = 1.0f;
float rate = 1.0f;
float volume = 1.0f;
int outputSampleRate = 0; /* Means use the input file sample rate. */
int emulateChordPitch = 0;
int quality = 0;
int xArg = 1;
int computeSpectrogram = 0;
int numRows = 0, numCols = 0;
while (xArg < argc && *(argv[xArg]) == '-') {
if (!strcmp(argv[xArg], "-c")) {
emulateChordPitch = 1;
printf("Scaling pitch linearly.\n");
} else if (!strcmp(argv[xArg], "-o")) {
xArg++;
if (xArg < argc) {
outputSampleRate = atoi(argv[xArg]);
printf("Setting output sample rate to %d\n", outputSampleRate);
}
} else if (!strcmp(argv[xArg], "-p")) {
xArg++;
if (xArg < argc) {
pitch = atof(argv[xArg]);
printf("Setting pitch to %0.2fX\n", pitch);
}
} else if (!strcmp(argv[xArg], "-q")) {
quality = 1;
printf("Disabling speed-up heuristics\n");
} else if (!strcmp(argv[xArg], "-r")) {
xArg++;
if (xArg < argc) {
rate = atof(argv[xArg]);
if (rate == 0.0f) {
usage();
}
printf("Setting rate to %0.2fX\n", rate);
}
} else if (!strcmp(argv[xArg], "-s")) {
xArg++;
if (xArg < argc) {
speed = atof(argv[xArg]);
printf("Setting speed to %0.2fX\n", speed);
}
#ifdef SONIC_SPECTROGRAM
} else if (!strcmp(argv[xArg], "-S")) {
xArg++;
if (xArg < argc) {
numCols = atof(argv[xArg]);
}
xArg++;
if (xArg < argc) {
numRows = atof(argv[xArg]);
computeSpectrogram = 1;
printf("Computing spectrogram %d wide and %d tall\n", numCols, numRows);
}
#endif /* SONIC_SPECTROGRAM */
} else if (!strcmp(argv[xArg], "-v")) {
xArg++;
if (xArg < argc) {
volume = atof(argv[xArg]);
printf("Setting volume to %0.2f\n", volume);
}
}
xArg++;
}
if (argc - xArg != 2) {
usage();
}
inFileName = argv[xArg];
outFileName = argv[xArg + 1];
runSonic(inFileName, outFileName, speed, pitch, rate, volume,
outputSampleRate, emulateChordPitch, quality,
computeSpectrogram, numRows, numCols);
return 0;
}