Allow using standard input/output pipes for processing

This commit is contained in:
oparviai 2008-12-25 12:38:45 +00:00
parent eb4c84fb14
commit c7bbaa4dbe
5 changed files with 813 additions and 724 deletions

View File

@ -18,7 +18,7 @@
</head>
<body class="normal">
<hr>
<h1>SoundTouch audio processing library v1.3.2pre
<h1>SoundTouch audio processing library v1.3.9 (1.4.0-pre)
</h1>
<p class="normal">SoundTouch library Copyright (c) Olli
Parviainen 2002-2008 </p>
@ -387,27 +387,29 @@ processing sound files.</p>
<h3>4.1. SoundStretch Usage Instructions</h3>
<p>SoundStretch Usage syntax:</p>
<blockquote>
<pre>soundstretch infile.wav outfile.wav [switches]</pre>
<pre>soundstretch infilename outfilename [switches]</pre>
</blockquote>
<p>Where: </p>
<table border="0" cellpadding="2" width="100%">
<tbody>
<tr>
<td valign="top">
<pre>&quot;infile.wav&quot;</pre>
<pre>&quot;infilename&quot;</pre>
</td>
<td valign="top">is the name of the input sound
data file (in .WAV audio file format). </td>
<td valign="top">Name of the input sound
data file (in .WAV audio file format). Give &quot;stdin&quot; as filename to use
standard input pipe. </td>
</tr>
<tr>
<td valign="top">
<pre>&quot;outfile.wav&quot;</pre>
<pre>&quot;outfilename&quot;</pre>
</td>
<td valign="top">is the name of the output sound
<td valign="top">Name of the output sound
file where the resulting sound is saved (in .WAV audio file format).
This parameter may be omitted if you&nbsp; don't want to save the
output
(e.g. when only calculating BPM rate with '-bpm' switch).</td>
(e.g. when only calculating BPM rate with '-bpm' switch). Give &quot;stdout&quot;
as filename to use standard output pipe.</td>
</tr>
<tr>
<td valign="top">
@ -447,10 +449,10 @@ n percents (n = -95.0 .. +5000.0 %) </td>
<pre>-bpm=n</pre>
</td>
<td valign="top">Detect the Beats-Per-Minute
(BPM) rate of the sound and adjust the tempo to meet 'n' BPMs. If this
switch is defined, the "-tempo=n" switch value is ignored. If "=n" is
omitted, i.e. switch &quot;-bpm&quot; is used alone, the program just calculates
and displays the BPM rate but doesn't adjust tempo according to the BPM
(BPM) rate of the sound and adjust the tempo to meet 'n' BPMs. When this switch is
applied, the &quot;-tempo&quot; switch is ignored. If "=n" is
omitted, i.e. switch &quot;-bpm&quot; is used alone, then the BPM rate is
estimated and displayed, but tempo not adjusted according to the BPM
value. </td>
</tr>
<tr>
@ -479,59 +481,61 @@ text (LGPL)</td>
</table>
<p>Notes:</p>
<ul>
<li>The numerical switch values can be entered
using either integer (e.g. "-tempo=123") or decimal (e.g.
<li>To use standard input/output pipes for processing, give &quot;stdin&quot;
and &quot;stdout&quot; as input/output filenames correspondingly. The
standard input/output pipes will still carry the audio data in .wav audio
file format.</li>
<li>The numerical switches allow both integer (e.g. "-tempo=123") and decimal (e.g.
"-tempo=123.45") numbers.</li>
<li>The &quot;-naa&quot; and/or "-quick" switches can be
used to reduce CPU usage while compromising some sound quality </li>
<li>The BPM detection algorithm works by detecting
repeating low-frequency (&lt;250H) sound patterns and thus works
mostly with most rock/pop music with bass or drum beat. The BPM
detection doesn't work on pieces such as classical music without
distinct, repeating bass frequency patterns. Also pieces with varying
tempo, varying bass patterns or very complex bass patterns (ja, hiphop) may produce odd BPM readings. <br>
<br>
In cases when the bass pattern drifts a bit around a nominal beat rate
(e.g. drummer is again drunken :), the BPM algorithm may report
incorrect harmonic one-halft to one-thirdth of the correct BPM value;
in such case the system could for example report BPM value of 50 or 100
instead of correct BPM value of 150. </li>
repeating bass or drum patterns at low frequencies of &lt;250Hz. A
lower-than-expected BPM figure may be reported for music with uneven or
complex bass patterns. </li>
</ul>
<h3>4.2. SoundStretch usage examples </h3>
<p><strong>Example 1</strong></p>
<p>The following command increases tempo of
the sound file &quot;originalfile.wav&quot; by 12.5% and saves
result to file &quot;destinationfile.wav&quot;:</p>
the sound file &quot;originalfile.wav&quot; by 12.5% and stores result to file &quot;destinationfile.wav&quot;:</p>
<blockquote>
<pre>soundstretch originalfile.wav destinationfile.wav -tempo=12.5</pre>
</blockquote>
<p><strong>Example 2</strong></p>
<p>The following command decreases the sound
pitch (key) of the sound file &quot;orig.wav&quot; by two
semitones and saves the result to file &quot;dest.wav&quot;:</p>
semitones and stores the result to file &quot;dest.wav&quot;:</p>
<blockquote>
<pre>soundstretch orig.wav dest.wav -pitch=-2</pre>
</blockquote>
<p><strong>Example 3</strong></p>
<p>The following command processes the file &quot;orig.wav&quot; by decreasing the sound tempo by 25.3% and
increasing the sound pitch (key) by 1.5 semitones. Result is
saved to file &quot;dest.wav&quot;:</p>
increasing the sound pitch (key) by 1.5 semitones. Resulting .wav audio data is
directed to standard output pipe:</p>
<blockquote>
<pre>soundstretch orig.wav dest.wav -tempo=-25.3 -pitch=1.5</pre>
<pre>soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5</pre>
</blockquote>
<p><strong>Example 4</strong></p>
<p>The following command detects the BPM rate
of the file &quot;orig.wav&quot; and adjusts the tempo to match
100 beats per minute. Result is saved to file &quot;dest.wav&quot;:</p>
100 beats per minute. Result is stored to file &quot;dest.wav&quot;:</p>
<blockquote>
<pre>soundstretch orig.wav dest.wav -bpm=100</pre>
</blockquote>
<p><strong>Example 5</strong></p>
<p>The following command reads .wav sound data from standard input pipe and
estimates the BPM rate:</p>
<blockquote>
<pre>soundstretch stdin -bpm</pre>
</blockquote>
<hr>
<h2>5. Change History</h2>
<h3>5.1. SoundTouch library Change History </h3>
<p><strong>v1.3.2:</strong></p>
<p><strong>v1.3.9 (1.4.0 pre):</strong></p>
<ul>
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
library</li>
<li>Bugfixes: Using uninitialied variables, GNU build scripts, compiler errors
due to 'const' keyword mismatch.</li>
<li>Some source code cleanup</li>
@ -617,6 +621,15 @@ files. </li>
<p>&nbsp;</p>
<h3>5.2. SoundStretch application Change
History </h3>
<p><strong>v1.3.9 (1.4.0 pre):</strong></p>
<ul>
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
library</li>
<li>Allow using standard input/output pipes for processing&nbsp;</li>
</ul>
<p><strong>v1.3.0:</strong></p>
<ul>
<li>Simplified accessing WAV files with floating

View File

@ -77,7 +77,10 @@ static const char whatText[] =
static const char usage[] =
"Usage :\n"
" soundstretch infile.wav outfile.wav [switches]\n\n"
" soundstretch infilename outfilename [switches]\n"
"\n"
"To use standard input/output pipes, give 'stdin' and 'stdout' as filenames.\n"
"\n"
"Available switches are:\n"
" -tempo=n : Change sound tempo by n percents (n=-95..+5000 %)\n"
" -pitch=n : Change sound pitch by n semitones (n=-60..+60 semitones)\n"

View File

@ -140,8 +140,6 @@ const static char dataStr[] = "data";
WavInFile::WavInFile(const char *fileName)
{
int hdrsOk;
// Try to open the file for reading
fptr = fopen(fileName, "rb");
if (fptr == NULL)
@ -153,22 +151,45 @@ WavInFile::WavInFile(const char *fileName)
throw runtime_error(msg);
}
init();
}
WavInFile::WavInFile(FILE *file)
{
// Try to open the file for reading
fptr = file;
if (!file)
{
// didn't succeed
string msg = "Error : Unable to access input stream for reading";
throw runtime_error(msg);
}
init();
}
/// Init the WAV file stream
void WavInFile::init()
{
int hdrsOk;
// assume file stream is already open
assert(fptr);
// Read the file headers
hdrsOk = readWavHeaders();
if (hdrsOk != 0)
{
// Something didn't match in the wav file headers
string msg = "File \"";
msg += fileName;
msg += "\" is corrupt or not a WAV file";
string msg = "Input file is corrupt or not a WAV file";
throw runtime_error(msg);
}
if (header.format.fixed != 1)
{
string msg = "File \"";
msg += fileName;
msg += "\" uses unsupported encoding.";
string msg = "Input file uses unsupported encoding.";
throw runtime_error(msg);
}
@ -537,6 +558,21 @@ WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int chann
}
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
{
bytesWritten = 0;
fptr = file;
if (fptr == NULL)
{
string msg = "Error : Unable to access output file stream.";
throw runtime_error(msg);
}
fillInHeader(sampleRate, bits, channels);
writeHeader();
}
WavOutFile::~WavOutFile()
{

View File

@ -105,6 +105,9 @@ private:
/// WAV header information
WavHeader header;
/// Init the WAV file stream
void init();
/// Read WAV file headers.
/// \return zero if all ok, nonzero if file format is invalid.
int readWavHeaders();
@ -125,6 +128,8 @@ public:
/// throws 'runtime_error' exception.
WavInFile(const char *filename);
WavInFile(FILE *file);
/// Destructor: Closes the file.
~WavInFile();
@ -222,6 +227,8 @@ public:
int channels ///< Number of channels (1=mono, 2=stereo)
);
WavOutFile(FILE *file, int sampleRate, int bits, int channels);
/// Destructor: Finalizes & closes the WAV file.
~WavOutFile();

View File

@ -49,12 +49,25 @@ using namespace std;
// Processing chunk size
#define BUFF_SIZE 2048
#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
// Not needed for GNU environment... ?
#define SET_STREAM_TO_BIN_MODE(f) ()
#endif
static const char _helloText[] =
"\n"
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2008\n"
"==================================================================\n"
"author e-mail: <oparviai@iki.fi> - WWW: http://www.surina.net/soundtouch\n"
"author e-mail: <oparviai"
"@"
"iki.fi> - WWW: http://www.surina.net/soundtouch\n"
"\n"
"This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
"more information.\n"
@ -64,8 +77,17 @@ static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParamet
{
int bits, samplerate, channels;
// open input file...
*inFile = new WavInFile(params->inFileName);
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);
}
// ... open output file with same sound parameters
bits = (*inFile)->getNumBits();
@ -74,7 +96,15 @@ static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParamet
if (params->outFileName)
{
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
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);
}
}
else
{
@ -107,27 +137,27 @@ static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunPar
if (params->outFileName)
{
#ifdef INTEGER_SAMPLES
printf("Uses 16bit integer sample type in processing.\n\n");
fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
#else
#ifndef FLOAT_SAMPLES
#error "Sampletype not defined"
#endif
printf("Uses 32bit floating point sample type in processing.\n\n");
fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
#endif
// print processing information only if outFileName given i.e. some processing will happen
printf("Processing the file with the following changes:\n");
printf(" tempo change = %+g %%\n", params->tempoDelta);
printf(" pitch change = %+g semitones\n", params->pitchDelta);
printf(" rate change = %+g %%\n\n", params->rateDelta);
printf("Working...");
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...");
}
else
{
// outFileName not given
printf("Warning: output file name missing, won't output anything.\n\n");
fprintf(stderr, "Warning: output file name missing, won't output anything.\n\n");
}
fflush(stdout);
fflush(stderr);
}
@ -194,8 +224,8 @@ static void detectBPM(WavInFile *inFile, RunParameters *params)
SAMPLETYPE sampleBuffer[BUFF_SIZE];
// detect bpm rate
printf("Detecting BPM rate...");
fflush(stdout);
fprintf(stderr, "Detecting BPM rate...");
fflush(stderr);
nChannels = inFile->getNumChannels();
@ -215,18 +245,18 @@ static void detectBPM(WavInFile *inFile, RunParameters *params)
// Now the whole song data has been analyzed. Read the resulting bpm.
bpmValue = bpm.getBpm();
printf("Done!\n");
fprintf(stderr, "Done!\n");
// rewind the file after bpm detection
inFile->rewind();
if (bpmValue > 0)
{
printf("Detected BPM rate %.1f\n\n", bpmValue);
fprintf(stderr, "Detected BPM rate %.1f\n\n", bpmValue);
}
else
{
printf("Couldn't detect BPM rate.\n\n");
fprintf(stderr, "Couldn't detect BPM rate.\n\n");
return;
}
@ -234,7 +264,7 @@ static void detectBPM(WavInFile *inFile, RunParameters *params)
{
// adjust tempo to given bpm
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
printf("The file will be converted to %.1f BPM\n\n", params->goalBPM);
fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM);
}
}
@ -247,7 +277,7 @@ int main(const int nParams, const char *paramStr[])
RunParameters *params;
SoundTouch SoundTouch;
printf(_helloText, SoundTouch::getVersionString());
fprintf(stderr, _helloText, SoundTouch::getVersionString());
try
{
@ -275,12 +305,12 @@ int main(const int nParams, const char *paramStr[])
delete outFile;
delete params;
printf("Done!\n");
fprintf(stderr, "Done!\n");
}
catch (runtime_error &e)
{
// An exception occurred during processing, display an error message
printf("%s\n", e.what());
fprintf(stderr, "%s\n", e.what());
return -1;
}