2008-02-10 17:24:28 +01:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///
|
|
|
|
/// SoundStretch main routine.
|
|
|
|
///
|
|
|
|
/// Author : Copyright (c) Olli Parviainen
|
|
|
|
/// Author e-mail : oparviai 'at' iki.fi
|
|
|
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
|
|
|
///
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2008-12-25 18:54:41 +01:00
|
|
|
// Last changed : $Date$
|
2008-02-10 17:24:28 +01:00
|
|
|
// File revision : $Revision: 4 $
|
|
|
|
//
|
2008-12-25 18:54:41 +01:00
|
|
|
// $Id$
|
2008-02-10 17:24:28 +01:00
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// License :
|
|
|
|
//
|
|
|
|
// SoundTouch audio processing library
|
|
|
|
// Copyright (c) Olli Parviainen
|
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
|
|
// License along with this library; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <stdio.h>
|
2008-12-25 17:33:39 +01:00
|
|
|
#include <string.h>
|
2008-02-10 17:24:28 +01:00
|
|
|
#include "RunParameters.h"
|
|
|
|
#include "WavFile.h"
|
|
|
|
#include "SoundTouch.h"
|
|
|
|
#include "BPMDetect.h"
|
|
|
|
|
|
|
|
using namespace soundtouch;
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
// Processing chunk size
|
|
|
|
#define BUFF_SIZE 2048
|
|
|
|
|
2008-12-25 13:38:45 +01:00
|
|
|
#if WIN32
|
|
|
|
#include <io.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
// Macro for Win32 standard input/output stream support: Sets a file stream into binary mode
|
|
|
|
#define SET_STREAM_TO_BIN_MODE(f) (_setmode(fileno(f), _O_BINARY))
|
|
|
|
#else
|
2008-12-25 17:33:39 +01:00
|
|
|
// Not needed for GNU environment...
|
|
|
|
#define SET_STREAM_TO_BIN_MODE(f) {}
|
2008-12-25 13:38:45 +01:00
|
|
|
#endif
|
|
|
|
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
static const char _helloText[] =
|
|
|
|
"\n"
|
2008-02-17 15:56:04 +01:00
|
|
|
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2008\n"
|
2008-02-10 17:24:28 +01:00
|
|
|
"==================================================================\n"
|
2008-12-25 13:38:45 +01:00
|
|
|
"author e-mail: <oparviai"
|
|
|
|
"@"
|
|
|
|
"iki.fi> - WWW: http://www.surina.net/soundtouch\n"
|
2008-02-10 17:24:28 +01:00
|
|
|
"\n"
|
|
|
|
"This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
|
|
|
|
"more information.\n"
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
|
|
|
|
{
|
|
|
|
int bits, samplerate, channels;
|
|
|
|
|
2008-12-25 13:38:45 +01:00
|
|
|
if (strcmp(params->inFileName, "stdin") == 0)
|
|
|
|
{
|
|
|
|
// used 'stdin' as input file
|
|
|
|
SET_STREAM_TO_BIN_MODE(stdin);
|
|
|
|
*inFile = new WavInFile(stdin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// open input file...
|
|
|
|
*inFile = new WavInFile(params->inFileName);
|
|
|
|
}
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// ... open output file with same sound parameters
|
2009-02-21 17:00:14 +01:00
|
|
|
bits = (int)(*inFile)->getNumBits();
|
|
|
|
samplerate = (int)(*inFile)->getSampleRate();
|
|
|
|
channels = (int)(*inFile)->getNumChannels();
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
if (params->outFileName)
|
|
|
|
{
|
2008-12-25 13:38:45 +01:00
|
|
|
if (strcmp(params->outFileName, "stdout") == 0)
|
|
|
|
{
|
|
|
|
SET_STREAM_TO_BIN_MODE(stdout);
|
|
|
|
*outFile = new WavOutFile(stdout, samplerate, bits, channels);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
|
|
|
|
}
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*outFile = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sets the 'SoundTouch' object up according to input file sound format &
|
|
|
|
// command line parameters
|
|
|
|
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
|
|
|
|
{
|
|
|
|
int sampleRate;
|
|
|
|
int channels;
|
|
|
|
|
2009-02-21 17:00:14 +01:00
|
|
|
sampleRate = (int)inFile->getSampleRate();
|
|
|
|
channels = (int)inFile->getNumChannels();
|
2008-02-10 17:24:28 +01:00
|
|
|
pSoundTouch->setSampleRate(sampleRate);
|
|
|
|
pSoundTouch->setChannels(channels);
|
|
|
|
|
|
|
|
pSoundTouch->setTempoChange(params->tempoDelta);
|
|
|
|
pSoundTouch->setPitchSemiTones(params->pitchDelta);
|
|
|
|
pSoundTouch->setRateChange(params->rateDelta);
|
|
|
|
|
|
|
|
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
|
2009-02-21 17:00:14 +01:00
|
|
|
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !(params->noAntiAlias));
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// print processing information
|
|
|
|
if (params->outFileName)
|
|
|
|
{
|
|
|
|
#ifdef INTEGER_SAMPLES
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
#else
|
|
|
|
#ifndef FLOAT_SAMPLES
|
|
|
|
#error "Sampletype not defined"
|
|
|
|
#endif
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
#endif
|
|
|
|
// print processing information only if outFileName given i.e. some processing will happen
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Processing the file with the following changes:\n");
|
|
|
|
fprintf(stderr, " tempo change = %+g %%\n", params->tempoDelta);
|
|
|
|
fprintf(stderr, " pitch change = %+g semitones\n", params->pitchDelta);
|
|
|
|
fprintf(stderr, " rate change = %+g %%\n\n", params->rateDelta);
|
|
|
|
fprintf(stderr, "Working...");
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// outFileName not given
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Warning: output file name missing, won't output anything.\n\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
|
2008-12-25 13:38:45 +01:00
|
|
|
fflush(stderr);
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Processes the sound
|
|
|
|
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
|
|
|
|
{
|
|
|
|
int nSamples;
|
|
|
|
int nChannels;
|
|
|
|
int buffSizeSamples;
|
|
|
|
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
|
|
|
|
|
|
|
if ((inFile == NULL) || (outFile == NULL)) return; // nothing to do.
|
|
|
|
|
2009-02-21 17:00:14 +01:00
|
|
|
nChannels = (int)inFile->getNumChannels();
|
|
|
|
assert(nChannels > 0);
|
2008-02-10 17:24:28 +01:00
|
|
|
buffSizeSamples = BUFF_SIZE / nChannels;
|
|
|
|
|
|
|
|
// Process samples read from the input file
|
|
|
|
while (inFile->eof() == 0)
|
|
|
|
{
|
|
|
|
int num;
|
|
|
|
|
|
|
|
// Read a chunk of samples from the input file
|
|
|
|
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
2009-02-21 17:00:14 +01:00
|
|
|
nSamples = num / (int)inFile->getNumChannels();
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// Feed the samples into SoundTouch processor
|
|
|
|
pSoundTouch->putSamples(sampleBuffer, nSamples);
|
|
|
|
|
|
|
|
// Read ready samples from SoundTouch processor & write them output file.
|
|
|
|
// NOTES:
|
|
|
|
// - 'receiveSamples' doesn't necessarily return any samples at all
|
|
|
|
// during some rounds!
|
|
|
|
// - On the other hand, during some round 'receiveSamples' may have more
|
|
|
|
// ready samples than would fit into 'sampleBuffer', and for this reason
|
|
|
|
// the 'receiveSamples' call is iterated for as many times as it
|
|
|
|
// outputs samples.
|
|
|
|
do
|
|
|
|
{
|
|
|
|
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
|
|
|
outFile->write(sampleBuffer, nSamples * nChannels);
|
|
|
|
} while (nSamples != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now the input file is processed, yet 'flush' few last samples that are
|
|
|
|
// hiding in the SoundTouch's internal processing pipeline.
|
|
|
|
pSoundTouch->flush();
|
|
|
|
do
|
|
|
|
{
|
|
|
|
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
|
|
|
|
outFile->write(sampleBuffer, nSamples * nChannels);
|
|
|
|
} while (nSamples != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
|
|
|
|
static void detectBPM(WavInFile *inFile, RunParameters *params)
|
|
|
|
{
|
|
|
|
float bpmValue;
|
|
|
|
int nChannels;
|
|
|
|
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
|
|
|
|
SAMPLETYPE sampleBuffer[BUFF_SIZE];
|
|
|
|
|
|
|
|
// detect bpm rate
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Detecting BPM rate...");
|
|
|
|
fflush(stderr);
|
2008-02-10 17:24:28 +01:00
|
|
|
|
2009-02-21 17:00:14 +01:00
|
|
|
nChannels = (int)inFile->getNumChannels();
|
2009-02-12 18:22:06 +01:00
|
|
|
assert(BUFF_SIZE % nChannels == 0);
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// Process the 'inFile' in small blocks, repeat until whole file has
|
|
|
|
// been processed
|
|
|
|
while (inFile->eof() == 0)
|
|
|
|
{
|
|
|
|
int num, samples;
|
|
|
|
|
|
|
|
// Read sample data from input file
|
|
|
|
num = inFile->read(sampleBuffer, BUFF_SIZE);
|
|
|
|
|
|
|
|
// Enter the new samples to the bpm analyzer class
|
|
|
|
samples = num / nChannels;
|
|
|
|
bpm.inputSamples(sampleBuffer, samples);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now the whole song data has been analyzed. Read the resulting bpm.
|
|
|
|
bpmValue = bpm.getBpm();
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Done!\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// rewind the file after bpm detection
|
|
|
|
inFile->rewind();
|
|
|
|
|
|
|
|
if (bpmValue > 0)
|
|
|
|
{
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Detected BPM rate %.1f\n\n", bpmValue);
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Couldn't detect BPM rate.\n\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params->goalBPM > 0)
|
|
|
|
{
|
|
|
|
// adjust tempo to given bpm
|
|
|
|
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM);
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-02-21 17:00:14 +01:00
|
|
|
int main(const int nParams, const char * const paramStr[])
|
2008-02-10 17:24:28 +01:00
|
|
|
{
|
|
|
|
WavInFile *inFile;
|
|
|
|
WavOutFile *outFile;
|
|
|
|
RunParameters *params;
|
2009-01-25 14:41:18 +01:00
|
|
|
SoundTouch soundTouch;
|
2008-02-10 17:24:28 +01:00
|
|
|
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, _helloText, SoundTouch::getVersionString());
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// Parse command line parameters
|
|
|
|
params = new RunParameters(nParams, paramStr);
|
|
|
|
|
|
|
|
// Open input & output files
|
|
|
|
openFiles(&inFile, &outFile, params);
|
|
|
|
|
|
|
|
if (params->detectBPM == TRUE)
|
|
|
|
{
|
|
|
|
// detect sound BPM (and adjust processing parameters
|
|
|
|
// accordingly if necessary)
|
|
|
|
detectBPM(inFile, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the 'SoundTouch' object for processing the sound
|
2009-01-25 14:41:18 +01:00
|
|
|
setup(&soundTouch, inFile, params);
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// Process the sound
|
2009-01-25 14:41:18 +01:00
|
|
|
process(&soundTouch, inFile, outFile);
|
2008-02-10 17:24:28 +01:00
|
|
|
|
|
|
|
// Close WAV file handles & dispose of the objects
|
|
|
|
delete inFile;
|
|
|
|
delete outFile;
|
|
|
|
delete params;
|
|
|
|
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "Done!\n");
|
2008-02-10 17:24:28 +01:00
|
|
|
}
|
2009-02-21 17:00:14 +01:00
|
|
|
catch (const runtime_error &e)
|
2008-02-10 17:24:28 +01:00
|
|
|
{
|
|
|
|
// An exception occurred during processing, display an error message
|
2008-12-25 13:38:45 +01:00
|
|
|
fprintf(stderr, "%s\n", e.what());
|
2008-02-10 17:24:28 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|