Learn about text to speech
Sunday, 22 April 2012
WavFile.h
////////////////////////////////////////////////////////////////////////////////
///
/// Classes for easy reading & writing of WAV sound files.
///
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
/// parse the WAV files with such processors.
///
/// Admittingly, more complete WAV reader routines may exist in public domain, but
/// the reason for 'yet another' one is that those generic WAV reader libraries are
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.7 $
//
// $Id: WavFile.h,v 1.7 2006/02/05 16:44:06 Olli Exp $
//
////////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef WAVFILE_H
#define WAVFILE_H
#include <stdio.h>
#ifndef uint
typedef unsigned int uint;
#endif
/// WAV audio file 'riff' section header
typedef struct
{
char riff_char[4];
int package_len;
char wave[4];
} WavRiff;
/// WAV audio file 'format' section header
typedef struct
{
char fmt[4];
int format_len;
short fixed;
short channel_number;
int sample_rate;
int byte_rate;
short byte_per_sample;
short bits_per_sample;
} WavFormat;
/// WAV audio file 'data' section header
typedef struct
{
char data_field[4];
uint data_len;
} WavData;
/// WAV audio file header
typedef struct
{
WavRiff riff;
WavFormat format;
WavData data;
} WavHeader;
/// Class for reading WAV audio files.
class WavInFile
{
private:
/// File pointer.
FILE *fptr;
/// Counter of how many bytes of sample data have been read from the file.
uint dataRead;
/// WAV header information
WavHeader header;
/// Read WAV file headers.
/// \return zero if all ok, nonzero if file format is invalid.
int readWavHeaders();
/// Checks WAV file header tags.
/// \return zero if all ok, nonzero if file format is invalid.
int checkCharTags();
/// Reads a single WAV file header block.
/// \return zero if all ok, nonzero if file format is invalid.
int readHeaderBlock();
/// Reads WAV file 'riff' block
int readRIFFBlock();
public:
/// Constructor: Opens the given WAV file. If the file can't be opened,
/// throws 'runtime_error' exception.
WavInFile(const char *filename);
/// Destructor: Closes the file.
~WavInFile();
/// Close the file. Notice that file is automatically closed also when the
/// class instance is deleted.
void close();
/// Rewind to beginning of the file
void rewind();
/// Get sample rate.
uint getSampleRate() const;
/// Get number of bits per sample, i.e. 8 or 16.
uint getNumBits() const;
/// Get sample data size in bytes. Ahem, this should return same information as
/// 'getBytesPerSample'...
uint getDataSizeInBytes() const;
/// Get total number of samples in file.
uint getNumSamples() const;
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
uint getBytesPerSample() const;
/// Get number of audio channels in the file (1=mono, 2=stereo)
uint getNumChannels() const;
/// Get the audio file length in milliseconds
uint getLengthMS() const;
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
/// Reads given number of elements from the file or if end-of-file reached, as many
/// elements as are left in the file.
///
/// \return Number of 8-bit integers read from the file.
int read(char *buffer, int maxElems);
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
/// of elements from the file or if end-of-file reached, as many elements as are
/// left in the file.
///
/// \return Number of 16-bit integers read from the file.
int read(short *buffer, ///< Pointer to buffer where to read data.
int maxElems ///< Size of 'buffer' array (number of array elements).
);
/// Reads audio samples from the WAV file to floating point format, converting
/// sample values to range [-1,1[. Reads given number of elements from the file
/// or if end-of-file reached, as many elements as are left in the file.
///
/// \return Number of elements read from the file.
int read(float *buffer, ///< Pointer to buffer where to read data.
int maxElems ///< Size of 'buffer' array (number of array elements).
);
/// Check end-of-file.
///
/// \return Nonzero if end-of-file reached.
int eof() const;
};
/// Class for writing WAV audio files.
class WavOutFile
{
private:
/// Pointer to the WAV file
FILE *fptr;
/// WAV file header data.
WavHeader header;
/// Counter of how many bytes have been written to the file so far.
int bytesWritten;
/// Fills in WAV file header information.
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
/// Finishes the WAV file header by supplementing information of amount of
/// data written to file etc
void finishHeader();
/// Writes the WAV file header.
void writeHeader();
public:
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
/// if file creation fails.
WavOutFile(const char *fileName, ///< Filename
int sampleRate, ///< Sample rate (e.g. 44100 etc)
int bits, ///< Bits per sample (8 or 16 bits)
int channels ///< Number of channels (1=mono, 2=stereo)
);
/// Destructor: Finalizes & closes the WAV file.
~WavOutFile();
/// Write data to WAV file. This function works only with 8bit samples.
/// Throws a 'runtime_error' exception if writing to file fails.
void write(const char *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
/// file fails.
void write(const short *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Write data to WAV file in floating point format, saturating sample values to range
/// [-1..+1[. Throws a 'runtime_error' exception if writing to file fails.
void write(const float *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Finalize & close the WAV file. Automatically supplements the WAV file header
/// information according to written data etc.
///
/// Notice that file is automatically closed also when the class instance is deleted.
void close();
};
#endif
Easy reading & writing of WAV sound files.
////////////////////////////////////////////////////////////////////////////////
///
/// Classes for easy reading & writing of WAV sound files.
///
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
/// parse the WAV files with such processors.
///
/// Admittingly, more complete WAV reader routines may exist in public domain,
/// but the reason for 'yet another' one is that those generic WAV reader
/// libraries are exhaustingly large and cumbersome! Wanted to have something
/// simpler here, i.e. something that's not already larger than rest of the
/// SoundTouch/SoundStretch program...
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.15 $
//
// $Id: WavFile.cpp,v 1.15 2006/02/05 16:44:06 Olli Exp $
//
////////////////////////////////////////////////////////////////////////////////
//
// 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 <stdio.h>
#include <stdexcept>
#include <string>
#include <assert.h>
#include <limits.h>
#include "WavFile.h"
using namespace std;
const static char riffStr[] = "RIFF";
const static char waveStr[] = "WAVE";
const static char fmtStr[] = "fmt ";
const static char dataStr[] = "data";
//////////////////////////////////////////////////////////////////////////////
//
// Helper functions for swapping byte order to correctly read/write WAV files
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
// turn-on the conversion if it appears necessary.
//
// For example, Intel x86 is little-endian and doesn't require conversion,
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
#ifdef BYTE_ORDER
// In gcc compiler detect the byte order automatically
#if BYTE_ORDER == BIG_ENDIAN
// big-endian platform.
#define _BIG_ENDIAN_
#endif
#endif
#ifdef _BIG_ENDIAN_
// big-endian CPU, swap bytes in 16 & 32 bit words
// helper-function to swap byte-order of 32bit integer
static inline void _swap32(unsigned int &dwData)
{
dwData = ((dwData >> 24) & 0x000000FF) |
((dwData >> 8) & 0x0000FF00) |
((dwData << 8) & 0x00FF0000) |
((dwData << 24) & 0xFF000000);
}
// helper-function to swap byte-order of 16bit integer
static inline void _swap16(unsigned short &wData)
{
wData = ((wData >> 8) & 0x00FF) |
((wData << 8) & 0xFF00);
}
// helper-function to swap byte-order of buffer of 16bit integers
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords)
{
unsigned long i;
for (i = 0; i < dwNumWords; i ++)
{
_swap16(pData[i]);
}
}
#else // BIG_ENDIAN
// little-endian CPU, WAV file is ok as such
// dummy helper-function
static inline void _swap32(unsigned int &dwData)
{
// do nothing
}
// dummy helper-function
static inline void _swap16(unsigned short &wData)
{
// do nothing
}
// dummy helper-function
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes)
{
// do nothing
}
#endif // BIG_ENDIAN
//////////////////////////////////////////////////////////////////////////////
//
// Class WavInFile
//
WavInFile::WavInFile(const char *fileName)
{
int hdrsOk;
// Try to open the file for reading
fptr = fopen(fileName, "rb");
if (fptr == NULL)
{
// didn't succeed
string msg = "Error : Unable to open file \"";
msg += fileName;
msg += "\" for reading.";
throw runtime_error(msg);
}
// 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";
throw runtime_error(msg);
}
if (header.format.fixed != 1)
{
string msg = "File \"";
msg += fileName;
msg += "\" uses unsupported encoding.";
throw runtime_error(msg);
}
dataRead = 0;
}
WavInFile::~WavInFile()
{
close();
}
void WavInFile::rewind()
{
int hdrsOk;
fseek(fptr, 0, SEEK_SET);
hdrsOk = readWavHeaders();
assert(hdrsOk == 0);
dataRead = 0;
}
int WavInFile::checkCharTags()
{
// header.format.fmt should equal to 'fmt '
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1;
// header.data.data_field should equal to 'data'
if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1;
return 0;
}
int WavInFile::read(char *buffer, int maxElems)
{
int numBytes;
uint afterDataRead;
// ensure it's 8 bit format
if (header.format.bits_per_sample != 8)
{
throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples.");
}
assert(sizeof(char) == 1);
numBytes = maxElems;
afterDataRead = dataRead + numBytes;
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
assert(numBytes >= 0);
}
numBytes = fread(buffer, 1, numBytes, fptr);
dataRead += numBytes;
return numBytes;
}
int WavInFile::read(short *buffer, int maxElems)
{
unsigned int afterDataRead;
int numBytes;
int numElems;
if (header.format.bits_per_sample == 8)
{
// 8 bit format
char *temp = new char[maxElems];
int i;
numElems = read(temp, maxElems);
// convert from 8 to 16 bit
for (i = 0; i < numElems; i ++)
{
buffer[i] = temp[i] << 8;
}
delete[] temp;
}
else
{
// 16 bit format
assert(header.format.bits_per_sample == 16);
assert(sizeof(short) == 2);
numBytes = maxElems * 2;
afterDataRead = dataRead + numBytes;
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
assert(numBytes >= 0);
}
numBytes = fread(buffer, 1, numBytes, fptr);
dataRead += numBytes;
numElems = numBytes / 2;
// 16bit samples, swap byte order if necessary
_swap16Buffer((unsigned short *)buffer, numElems);
}
return numElems;
}
int WavInFile::read(float *buffer, int maxElems)
{
short *temp = new short[maxElems];
int num;
int i;
double fscale;
num = read(temp, maxElems);
fscale = 1.0 / 32768.0;
// convert to floats, scale to range [-1..+1[
for (i = 0; i < num; i ++)
{
buffer[i] = (float)(fscale * (double)temp[i]);
}
delete[] temp;
return num;
}
int WavInFile::eof() const
{
// return true if all data has been read or file eof has reached
return (dataRead == header.data.data_len || feof(fptr));
}
void WavInFile::close()
{
fclose(fptr);
fptr = NULL;
}
// test if character code is between a white space ' ' and little 'z'
static int isAlpha(char c)
{
return (c >= ' ' && c <= 'z') ? 1 : 0;
}
// test if all characters are between a white space ' ' and little 'z'
static int isAlphaStr(char *str)
{
int c;
c = str[0];
while (c)
{
if (isAlpha(c) == 0) return 0;
str ++;
c = str[0];
}
return 1;
}
int WavInFile::readRIFFBlock()
{
fread(&(header.riff), sizeof(WavRiff), 1, fptr);
// swap 32bit data byte order if necessary
_swap32((unsigned int &)header.riff.package_len);
// header.riff.riff_char should equal to 'RIFF');
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
// header.riff.wave should equal to 'WAVE'
if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1;
return 0;
}
int WavInFile::readHeaderBlock()
{
char label[5];
string sLabel;
// lead label string
fread(label, 1, 4, fptr);
label[4] = 0;
if (isAlphaStr(label) == 0) return -1; // not a valid label
// Decode blocks according to their label
if (strcmp(label, fmtStr) == 0)
{
int nLen, nDump;
// 'fmt ' block
memcpy(header.format.fmt, fmtStr, 4);
// read length of the format field
fread(&nLen, sizeof(int), 1, fptr);
// swap byte order if necessary
_swap32((unsigned int &)nLen); // int format_len;
header.format.format_len = nLen;
// calculate how much length differs from expected
nDump = nLen - (sizeof(header.format) - 8);
// if format_len is larger than expected, read only as much data as we've space for
if (nDump > 0)
{
nLen = sizeof(header.format) - 8;
}
// read data
fread(&(header.format.fixed), nLen, 1, fptr);
// swap byte order if necessary
_swap16((unsigned short &)header.format.fixed); // short int fixed;
_swap16((unsigned short &)header.format.channel_number); // short int channel_number;
_swap32((unsigned int &)header.format.sample_rate); // int sample_rate;
_swap32((unsigned int &)header.format.byte_rate); // int byte_rate;
_swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample;
_swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample;
// if format_len is larger than expected, skip the extra data
if (nDump > 0)
{
fseek(fptr, nDump, SEEK_CUR);
}
return 0;
}
else if (strcmp(label, dataStr) == 0)
{
// 'data' block
memcpy(header.data.data_field, dataStr, 4);
fread(&(header.data.data_len), sizeof(uint), 1, fptr);
// swap byte order if necessary
_swap32((unsigned int &)header.data.data_len);
return 1;
}
else
{
uint len, i;
uint temp;
// unknown block
// read length
fread(&len, sizeof(len), 1, fptr);
// scan through the block
for (i = 0; i < len; i ++)
{
fread(&temp, 1, 1, fptr);
if (feof(fptr)) return -1; // unexpected eof
}
}
return 0;
}
int WavInFile::readWavHeaders()
{
int res;
memset(&header, 0, sizeof(header));
res = readRIFFBlock();
if (res) return 1;
// read header blocks until data block is found
do
{
// read header blocks
res = readHeaderBlock();
if (res < 0) return 1; // error in file structure
} while (res == 0);
// check that all required tags are legal
return checkCharTags();
}
uint WavInFile::getNumChannels() const
{
return header.format.channel_number;
}
uint WavInFile::getNumBits() const
{
return header.format.bits_per_sample;
}
uint WavInFile::getBytesPerSample() const
{
return getNumChannels() * getNumBits() / 8;
}
uint WavInFile::getSampleRate() const
{
return header.format.sample_rate;
}
uint WavInFile::getDataSizeInBytes() const
{
return header.data.data_len;
}
uint WavInFile::getNumSamples() const
{
return header.data.data_len / header.format.byte_per_sample;
}
uint WavInFile::getLengthMS() const
{
uint numSamples;
uint sampleRate;
numSamples = getNumSamples();
sampleRate = getSampleRate();
assert(numSamples < UINT_MAX / 1000);
return (1000 * numSamples / sampleRate);
}
//////////////////////////////////////////////////////////////////////////////
//
// Class WavOutFile
//
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
{
bytesWritten = 0;
fptr = fopen(fileName, "wb");
if (fptr == NULL)
{
string msg = "Error : Unable to open file \"";
msg += fileName;
msg += "\" for writing.";
//pmsg = msg.c_str;
throw runtime_error(msg);
}
fillInHeader(sampleRate, bits, channels);
writeHeader();
}
WavOutFile::~WavOutFile()
{
close();
}
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
{
// fill in the 'riff' part..
// copy string 'RIFF' to riff_char
memcpy(&(header.riff.riff_char), riffStr, 4);
// package_len unknown so far
header.riff.package_len = 0;
// copy string 'WAVE' to wave
memcpy(&(header.riff.wave), waveStr, 4);
// fill in the 'format' part..
// copy string 'fmt ' to fmt
memcpy(&(header.format.fmt), fmtStr, 4);
header.format.format_len = 0x10;
header.format.fixed = 1;
header.format.channel_number = (short)channels;
header.format.sample_rate = sampleRate;
header.format.bits_per_sample = (short)bits;
header.format.byte_per_sample = (short)(bits * channels / 8);
header.format.byte_rate = header.format.byte_per_sample * sampleRate;
header.format.sample_rate = sampleRate;
// fill in the 'data' part..
// copy string 'data' to data_field
memcpy(&(header.data.data_field), dataStr, 4);
// data_len unknown so far
header.data.data_len = 0;
}
void WavOutFile::finishHeader()
{
// supplement the file length into the header structure
header.riff.package_len = bytesWritten + 36;
header.data.data_len = bytesWritten;
writeHeader();
}
void WavOutFile::writeHeader()
{
WavHeader hdrTemp;
// swap byte order if necessary
hdrTemp = header;
_swap32((unsigned int &)hdrTemp.riff.package_len);
_swap32((unsigned int &)hdrTemp.format.format_len);
_swap16((unsigned short &)hdrTemp.format.fixed);
_swap16((unsigned short &)hdrTemp.format.channel_number);
_swap32((unsigned int &)hdrTemp.format.sample_rate);
_swap32((unsigned int &)hdrTemp.format.byte_rate);
_swap16((unsigned short &)hdrTemp.format.byte_per_sample);
_swap16((unsigned short &)hdrTemp.format.bits_per_sample);
_swap32((unsigned int &)hdrTemp.data.data_len);
// write the supplemented header in the beginning of the file
fseek(fptr, 0, SEEK_SET);
fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
// jump back to the end of the file
fseek(fptr, 0, SEEK_END);
}
void WavOutFile::close()
{
finishHeader();
fclose(fptr);
fptr = NULL;
}
void WavOutFile::write(const char *buffer, int numElems)
{
int res;
if (header.format.bits_per_sample != 8)
{
throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples.");
}
assert(sizeof(char) == 1);
res = fwrite(buffer, 1, numElems, fptr);
if (res != numElems)
{
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += numElems;
}
void WavOutFile::write(const short *buffer, int numElems)
{
int res;
// 16 bit samples
if (numElems < 1) return; // nothing to do
if (header.format.bits_per_sample == 8)
{
int i;
char *temp = new char[numElems];
// convert from 16bit format to 8bit format
for (i = 0; i < numElems; i ++)
{
temp[i] = buffer[i] >> 8;
}
// write in 8bit format
write(temp, numElems);
delete[] temp;
}
else
{
// 16bit format
unsigned short *pTemp = new unsigned short[numElems];
assert(header.format.bits_per_sample == 16);
// allocate temp buffer to swap byte order if necessary
memcpy(pTemp, buffer, numElems * 2);
_swap16Buffer(pTemp, numElems);
res = fwrite(pTemp, 2, numElems, fptr);
delete[] pTemp;
if (res != numElems)
{
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += 2 * numElems;
}
}
void WavOutFile::write(const float *buffer, int numElems)
{
int i;
short *temp = new short[numElems];
int iTemp;
// convert to 16 bit integer
for (i = 0; i < numElems; i ++)
{
// convert to integer
iTemp = (int)(32768.0f * buffer[i]);
// saturate
if (iTemp < -32768) iTemp = -32768;
if (iTemp > 32767) iTemp = 32767;
temp[i] = (short)iTemp;
}
write(temp, numElems);
delete[] temp;
}
Simple SOLA algorithm Main.cpp
/////////////////////////////////////////////////////////////////////
//
// Simple SOLA algorithm example. The example reads a .wav sound
// file with mono-16bit-44100Hz sample format, process it with SOLA
// and writes output into another .wav file.
//
// Copyright (c) Olli Parviainen 2006 <oparviai@iki.fi>
//
/////////////////////////////////////////////////////////////////////
#include <stdexcept>
#include "wavfile.h"
using namespace std;
// Time scaling factor, values > 1.0 increase, values < 1.0 decrease tempo
#define TIME_SCALE 1 // 15% slower tempo
// Processing sequence size (100 msec with 44100Hz samplerate)
#define SEQUENCE 800//4410
// Overlapping size (20 msec)
#define OVERLAP 160//882
// Best overlap offset seeking window (15 msec)
#define SEEK_WINDOW 120//662
// Processing sequence flat mid-section duration
#define FLAT_DURATION (SEQUENCE - 2 * (OVERLAP))
// Theoretical interval between the processing seqeuences
#define SEQUENCE_SKIP ((int)((SEQUENCE - OVERLAP) * (TIME_SCALE)))
typedef short SAMPLE; // sample type, 16bit signed integer
// Use cross-correlation function to find best overlapping offset
// where input_prev and input_new match best with each other
int seek_best_overlap(const SAMPLE *input_prev, const SAMPLE *input_new)
{
int i;
int bestoffset = 0;
float bestcorr = -1e30f;
float temp[OVERLAP];
// Precalculate overlapping slopes with input_prev
for (i = 0; i < OVERLAP; i ++)
{
temp[i] = (float)(input_prev[i] * i * (OVERLAP - i));
}
// Find best overlap offset within [0..SEEK_WINDOW]
for (i = 0; i < SEEK_WINDOW; i ++)
{
int j;
float crosscorr = 0;
for (j = 0; j < OVERLAP; j ++)
{
crosscorr += (float)input_new[i + j] * temp[j];
}
if (crosscorr > bestcorr)
{
// found new best offset candidate
bestcorr = crosscorr;
bestoffset = i;
}
}
return bestoffset;
}
// Overlap 'input_prev' with 'input_new' by sliding the amplitudes during
// OVERLAP samples. Store result to 'output'.
void overlap(SAMPLE *output, const SAMPLE *input_prev, const SAMPLE *input_new)
{
int i;
for (i = 0; i < OVERLAP; i ++)
{
output[i] = (input_prev[i] * (OVERLAP - i) + input_new[i] * i) / OVERLAP;
}
}
// SOLA algorithm. Performs time scaling for sample data given in 'input',
// write result to 'output'. Return number of output samples.
int sola(SAMPLE *output, const SAMPLE *input, int num_in_samples)
{
int num_out_samples = 0;
const SAMPLE *seq_offset = input;
const SAMPLE *prev_offset;
int nTest = SEQUENCE_SKIP;
while (num_in_samples > SEQUENCE_SKIP + SEEK_WINDOW)
{
// copy flat mid-sequence from current processing sequence to output
memcpy(output, seq_offset, FLAT_DURATION * sizeof(SAMPLE));
// calculate a pointer to overlap at end of the processing sequence
prev_offset = seq_offset + FLAT_DURATION;
// update input pointer to theoretical next processing sequence begin
input += SEQUENCE_SKIP - OVERLAP;
// seek actual best matching offset using cross-correlation
seq_offset = input + seek_best_overlap(prev_offset, input);
// do overlapping between previous & new sequence, copy result to output
overlap(output + FLAT_DURATION, prev_offset, seq_offset);
// Update input & sequence pointers by overlapping amount
seq_offset += OVERLAP;
input += OVERLAP;
// Update output pointer & sample counters
output += SEQUENCE - OVERLAP;
num_out_samples += SEQUENCE - OVERLAP;
num_in_samples -= SEQUENCE_SKIP;
}
return num_out_samples;
}
// Buffers for input/output sample data. For sake of simplicity, these are
// just made 'big enough' for the example purpose.
SAMPLE inbuffer[10240000];
SAMPLE outbuffer[20240000];
int main(int numstr, char **pstr)
{
if (numstr < 3)
{
printf("usage: solatest input.wav output.wav\n");
return -1;
}
try
{
int insamples, outsamples;
// Open input file
WavInFile infile(pstr[1]);
if ((infile.getSampleRate() != 44100) || (infile.getNumChannels() != 1))
{
printf("Sorry, this example processes mono audio sampled at 44100Hz.\n");
return -1;
}
// Read data from input file
insamples = infile.read(inbuffer, 10240000);
// Process
outsamples = sola(outbuffer, inbuffer, insamples);
// Write result to output file
WavOutFile outfile(pstr[2], infile.getSampleRate(), infile.getNumBits(), infile.getNumChannels());
outfile.write(outbuffer, outsamples);
}
catch (exception &e)
{
printf("Error: %s\n", e.what());
}
return 0;
}
SOLA.M MATHLAB
sa=585;ss=438; %ÕâÊÇ?µµ?µÄ
w=512;
wov=w-ss;kmax=500;
x=wavread('v017');
%*********** time scaling **************
xst=1;yout=[];
xbuff=x(sa:sa+w-1);
st=sa:sa:length(x); %?ªÊ?Ê?ÓïÒô?Î?ÓSa???ªÊ?ÊäÈë,ÒÔºóÃ??ÎÏòºóÒÆ??Sa?öµã
r=mod(length(x),sa);
num=(length(x)-r)/sa; %?Ü??Òª?øÐÐnumÂÖ?Ù??
x=[x; zeros(w+kmax,1)];%ÓïÒôÎ??ÎÐèÒª??0??ÒòΪ?ÖÎö???ÚW?áÒÆ????ÇÒ?î?óÒÆ??kmax?öµã
for j=1:(num)
y=x(xst:1:xst+w-1); %?ªÊ?Ê?ÓïÒô?Î?ÓSa??
start=st(j):st(j)+kmax-1; %?ÖÎö???ÚµÄÆðµã???î?àÒÆ??kmax?öµã
cy=y(end:-1:end-wov+1); %È??öÊä?öÐòÁÐyµÄºówov?öµã
cy=cy(end:-1:1);
km_buf=zeros(1,kmax); %ÓÃÀ??ÇÂ?kmax?ö??Ïà?ØϵÊý
for i=1:kmax
xbuff=x(start(i):start(i)+w-1); %?ÖÎö???ÚËù?ØÈ?µÄw?öµã
cx=xbuff(1:wov); %È??öÇ?wov?öµã
rxx_k=sum(cx.^2);
rxy_k=sum(cx.*cy);
if ( rxx_k==0) %ÈôΪÁã???íÊ?Òѵ?ÓïÒôÎ??Î?Õ?Å??0µÄ???Ö??ÔòÍ?Ö?
kmbuf(i)=0;
break;
else
km_buf(i)=(rxy_k.^2)./rxx_k;
end
end
km=find(km_buf==max(km_buf)); %ÕÒ?ö?î?óµÄ??Ïà?ØϵÊýÔÚkm_bufµÄÎ?ÖÃ
yout=[yout; x(start(km)+wov:start(km)+w+1)]; %?ÑÒ?ÖÂÐÔ?îºÃµÄÐòÁеÄSs?öµã??Ϊ?îºóÊä?ö
xst=xst+sa;
end
%************?ä?ÉÑùÂÊ************************
L=sa;M=ss;
data=[];
data_out=[];
y_end=length(yout);
y_st=0;
for j=1:L;
for i=1:M;
y_st=y_st+1;
if y_st<y_end
invert=linspace(yout(y_st),yout(y_st+1),L+1);%ÔÚÃ?Á??öµãÖ??äÏßÐÎ?åÈëL-1?öµã
elseif y_st==y_end
invert=linspace(yout(y_st),0,L+1); %ÈôÊÇ?îºóÒ??öµã??ÔòËüÓëÁãÖ??ä?åÈëL-1?öµã
else
break;
end
data=[data invert(1:end-1)]; %Ã??Î?ÑL?öµãÊä?öµ?data
end
data_out=[data_out data(1:M:end)]; %ÔÚdataÖÐÃ??ôM?öµãÈ??öÒ??öµã????Êä?öµ?data_out
data=[]; %?ÑdataµÄÄÚÈÝÇå?ý
end
Pitch Synchronous Overlap Add Method (PSOLA.CPP)
PSOLA.CPP
#include "../common/tdpsola.h"
#include "psola.h"
CPSOLA instance;
void PSOLA_EnableCosineSmooth(bool enable)
{
instance.enableCosineSmooth(enable);
}
void PSOLA_SetSpectralMapping(bool useBezier, int x1, int y1, int x2, int y2)
{
instance.setSpectralMapping(useBezier, x1, y1, x2, y2);
}
bool PSOLA_IsCosineSmoothEnabled()
{
return instance.isCosineSmoothEnabled();
}
void PSOLA_EnableVoicelessExtension(int method)
{
instance.setVoicelessExtension(method);
}
int PSOLA_GetVoicelessExtension()
{
return instance.getVoicelessExtension();
}
unsigned PSOLA_ModifyPitchContour(
const short * srcWave,
unsigned srcLength,
const unsigned *srcTags,
unsigned tagNumber,
const unsigned *trgPeriods,
unsigned periodNumber,
unsigned trgDuration,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate)
{
return instance.modifyPitchContour(srcWave, srcLength, srcTags, tagNumber, trgWave, trgBufferLength, trgPeriods, periodNumber, trgDuration, specRatio, sampleRate);
}
unsigned PSOLA_Modify(
const short * srcWave,
unsigned srcLength,
const unsigned *srcTags,
unsigned tagNumber,
unsigned trgPitch,
unsigned trgDuration,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate)
{
return instance.modify(srcWave, srcLength, srcTags, tagNumber, trgWave, trgBufferLength, trgPitch, trgDuration, specRatio, sampleRate);
}
unsigned PSOLA_ModifyRatio(
const short * srcWave,
unsigned srcLength,
const unsigned * srcTags,
unsigned tagNumber,
float pitchRatio,
float durationRatio,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate
)
{
return instance.modifyRatio(srcWave, srcLength, srcTags, tagNumber, trgWave, trgBufferLength, pitchRatio, durationRatio, specRatio, sampleRate);
}
PSOLA.H
///
/// Modify wave using TP-PSOLA algorithm
///
/// @version 1.0.0
/// @author Jun Xu
/// @date 2007/07/18
///
#ifndef _CST_PSOLA_PSOLA_H_
#define _CST_PSOLA_PSOLA_H_
#ifndef PSOLA_EXPORTS
# define PSOLA_DLL_EXPORTS __declspec(dllimport)
# ifdef _DEBUG
# pragma comment(lib, "psolad.lib")
# pragma message("Linking with psolad.dll")
# else
# pragma comment(lib, "psola.lib")
# pragma message("Linking with psola.dll")
# endif
#else
# define PSOLA_DLL_EXPORTS __declspec(dllexport)
#endif
#ifdef _cplusplus
extern "C" {
#endif
#define PSOLA_VLPPMETHOD_NONE 0 ///< ÇåÒô¶Î²»×ö»ùƵÀ©Õ¹
#define PSOLA_VLPPMETHOD_FIXED 1 ///< ÇåÒô¶Î×ö¹Ì¶¨ÖÜÆڵĻùƵÀ©Õ¹
#define PSOLA_VLPPMETHOD_EQUAL 2 ///< ÇåÒô¶Î×öÓëµÚÒ»¸öÖÜÆÚÏàµÈµÄµÈÖÜÆÚÀ©Õ¹
#define PSOLA_VLPPMETHOD_PEAK 3 ///< ÇåÒô¶Î¸ù¾ÝÓïÒô¼â·åµãÀ´×öÖÜÆÚÀ©Õ¹
#define PSOLA_VLPPMETHOD_AUTO 4 ///< ×Ô¶¯×öÇåÒôÀ©Õ¹£¨¸ù¾Ýʱ³¤Ð޸ıÈÀý¾ö¶¨£©
#define PSOLA_VLPPMETHOD_MAX 4
///
/// ÉèÖÃÐ޸Ĺý³ÌÖеÄƵÆ×Ó³É䷽ʽ£¬Ð§¹û²»ºÃ£¬É÷ÓÃ
///
/// @param useBezier true:ʹÓñ´Èû¶ûÇúÏß,false:ʹÓÃÕÛÏß
/// @param x1,y1 µÚÒ»¸ö¿ØÖƵãµÄ×ø±ê
/// @param x2,y2 µÚ¶þ¸ö¿ØÖƵãµÄ×ø±ê
///
PSOLA_DLL_EXPORTS void PSOLA_SetSpectralMapping(bool useBezier, int x1, int y1, int x2, int y2);
///
/// ÉèÖÃÇåÒô¶ÎÖÜÆÚÀ©Õ¹·½Ê½
///
/// @param method 0-3£¬²Î¼ûÉÏÃæµÄºê¶¨Òå
///
PSOLA_DLL_EXPORTS void PSOLA_EnableVoicelessExtension(int method);
/// »ñÈ¡ÇåÒô¶ÎÖÜÆÚÀ©Õ¹·½Ê½
PSOLA_DLL_EXPORTS int PSOLA_GetVoicelessExtension();
///
/// ÆôÓÃÓàÏÒº¯Êý½øÐÐÆ´½Ó±ß½çƽ»¬
///
/// @param enable true:ÆôÓÃ,false:²»ÆôÓÃ
///
PSOLA_DLL_EXPORTS void PSOLA_EnableCosineSmooth(bool enable);
/// ÅжÏÓàÏұ߽çƽ»¬ÊÇ·ñ±»ÆôÓÃ
PSOLA_DLL_EXPORTS bool PSOLA_IsCosineSmoothEnabled();
///
/// Modify wave using PSOLA model
/// ʹÓÃPSOLAÄ£ÐͽøÐÐÓïÒôÐ޸ģ¬Ö¸¶¨Ä¿±êµÄƽ¾ù»ùƵÖÜÆÚÒÔ¼°ÓïÒô³¤¶È
///
/// @param srcWave[in] wave buffer read from speech database
/// ÓïÒôÊý¾Ý£¬±ØÐëΪ16bit²ÉÑù¾«¶È
/// @param srcLength[in] wave buffer length, in short count
/// ÓïÒôÊý¾ÝµÄ²ÉÑùµã¸öÊý
/// @param srcTags[in] peak tags read from speech database
/// each tag indicate the peak position offset to the first sample of wave
/// ÓïÒôÊý¾ÝµÄ·åÖµµã±ê×¢Êý×é
/// ÄÚ²¿±£´æÿ¸ö·åÖµµãÏà¶ÔÓïÒôÆðʼµãµÄÆ«ÒÆλÖÃ
/// @param tagNumber[in] peak tag count of srcTags
/// ·åÖµ±ê×¢¸öÊý
/// @param trgPitch[in] predicted average pitch period
/// trgPitch=0 means keeping pitch no change
/// Ä¿±ê»ùƵÖÜÆڵĴóС£¬Èç¹ûΪ0Ôò±íʾ²»½øÐÐÐÞ¸Ä
/// @param trgDuration[in] predicted wave duration, in short
/// Ä¿±êÓïÒô²ÉÑùµã¸öÊý£¬Èç¹ûΪ0Ôò±íʾ²»½øÐÐÐÞ¸Ä
/// @param specRatio [in] modification ratio of spectra
/// ƵÆ×Ð޸ıÈÀý£¬0Ϊ²»ÐÞ¸Ä
/// @param trgWave[out] modified wave, buffer should be allocated outside
/// Ä¿±êÓïÒôÊý¾Ý»º³åÇø£¬ÓÉÍⲿ·ÖÅ䣬Îñ±Ø±ÈtrgDurationÒª´óһЩ
/// @param sampleRate[in] Sample count per second, default is 16000
/// ²ÉÑùÂÊ£¬Ò»°ãÇëʹÓÃ16000
///
/// @return true if modified successfully
/// false if not, then the content of trgWave if un-defined
///
PSOLA_DLL_EXPORTS unsigned PSOLA_Modify(
const short * srcWave,
unsigned srcLength,
const unsigned *srcTags,
unsigned tagNumber,
unsigned trgPitch,
unsigned trgDuration,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate);
///
/// Modify wave using PSOLA model
/// ʹÓÃPSOLAÄ£ÐͽøÐÐÓïÒôÐ޸ģ¬Ö¸¶¨Ä¿±ê»ùƵÇúÏß
///
/// @param srcWave[in] wave buffer read from speech database
/// ÓïÒôÊý¾Ý£¬±ØÐëΪ16bit²ÉÑù¾«¶È
/// @param srcLength[in] wave buffer length, in short count
/// ÓïÒôÊý¾ÝµÄ²ÉÑùµã¸öÊý
/// @param srcTags[in] peak tags read from speech database
/// each tag indicate the peak position offset to the first sample of wave
/// ÓïÒôÊý¾ÝµÄ·åÖµµã±ê×¢Êý×é
/// ÄÚ²¿±£´æÿ¸ö·åÖµµãÏà¶ÔÓïÒôÆðʼµãµÄÆ«ÒÆλÖÃ
/// @param tagNumber[in] peak tag count of srcTags
/// ·åÖµ±ê×¢¸öÊý
/// @param trgPeriods[in] predicted pitch period
/// Ä¿±ê»ùƵÖÜÆÚÊý×é
/// @param periodNumber[in] pitch period count of target
/// Ä¿±ê»ùƵÖÜÆÚÊýÄ¿
/// @param trgDuration[in] predicted wave duration, in short
/// Ä¿±êÓïÒô²ÉÑùµã¸öÊý£¬Èç¹ûΪ0Ôò±íʾ²»½øÐÐÐÞ¸Ä
/// @param specRatio [in] modification ratio of spectra
/// ƵÆ×Ð޸ıÈÀý£¬0Ϊ²»ÐÞ¸Ä
/// @param trgWave[out] modified wave, buffer should be allocated outside
/// Ä¿±êÓïÒôÊý¾Ý»º³åÇø£¬ÓÉÍⲿ·ÖÅ䣬Îñ±Ø±ÈtrgDurationÒª´óһЩ
/// @param sampleRate[in] Sample count per second, default is 16000
/// ²ÉÑùÂÊ£¬Ò»°ãÇëʹÓÃ16000
///
/// @return true if modified successfully
/// false if not, then the content of trgWave if un-defined
///
PSOLA_DLL_EXPORTS unsigned PSOLA_ModifyPitchContour(
const short * srcWave,
unsigned srcLength,
const unsigned *srcTags,
unsigned tagNumber,
const unsigned *trgPeriods,
unsigned periodNumber,
unsigned trgDuration,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate);
///
/// Modify wave using PSOLA model
/// ʹÓÃPSOLAÄ£ÐͽøÐÐÓïÒôÐ޸ģ¬Ö¸¶¨ÖÜÆÚ£¬Ê±³¤µÄÐ޸ıÈÀý
///
/// @param srcWave[in] wave buffer read from speech database
/// ÓïÒôÊý¾Ý£¬±ØÐëΪ16bit²ÉÑù¾«¶È
/// @param srcLength[in] wave buffer length, in short count
/// ÓïÒôÊý¾ÝµÄ²ÉÑùµã¸öÊý
/// @param srcTags[in] peak tags read from speech database
/// each tag indicate the peak position offset to the first sample of wave
/// ÓïÒôÊý¾ÝµÄ·åÖµµã±ê×¢Êý×é
/// ÄÚ²¿±£´æÿ¸ö·åÖµµãÏà¶ÔÓïÒôÆðʼµãµÄÆ«ÒÆλÖÃ
/// @param tagNumber[in] peak tag count of srcTags
/// ·åÖµ±ê×¢¸öÊý
/// @param pitchRatio[in] modification ratio of pitch
/// Ä¿±ê»ùƵÖÜÆÚÐ޸ıÈÀý£¬Èç¹ûΪ0Ôò±íʾ²»½øÐÐÐÞ¸Ä
/// @param durationRatio[in]modification ratio of duration
/// Ä¿±êÓïÒôʱ³¤Ð޸ıÈÀý£¬Èç¹ûΪ0Ôò±íʾ²»½øÐÐÐÞ¸Ä
/// @param specRatio [in] modification ratio of spectra
/// ƵÆ×Ð޸ıÈÀý£¬0Ϊ²»ÐÞ¸Ä
/// @param trgWave[out] modified wave, buffer should be allocated outside
/// Ä¿±êÓïÒôÊý¾Ý»º³åÇø£¬ÓÉÍⲿ·ÖÅ䣬Îñ±Ø±ÈtrgDurationÒª´óһЩ
/// @param sampleRate[in] Sample count per second, default is 16000
/// ²ÉÑùÂÊ£¬Ò»°ãÇëʹÓÃ16000
///
/// @return true if modified successfully
/// false if not, then the content of trgWave if un-defined
///
PSOLA_DLL_EXPORTS unsigned PSOLA_ModifyRatio(
const short * srcWave,
unsigned srcLength,
const unsigned *srcTags,
unsigned tagNumber,
float pitchRatio,
float durationRatio,
float specRatio,
short * trgWave,
unsigned trgBufferLength,
unsigned sampleRate);
#ifdef _cplusplus
}
#endif
#endif
PSOLA. h Header
#ifndef PSOLA_H_
#define PSOLA_H_
#include <vector>
#include "DSP.h"
using namespace std;
class CPsola{
public:
CPsola();
CPsola(short*,unsigned);
void SetData(short*,unsigned);
void SetAmplitudeMultiple(float);
void SetDuration(float);
void SetPitch(float*,unsigned,float);
void SetNewPitch(float*,unsigned);
void SetSampleFrequency(unsigned);
void SetFrameLength(float);
void SetX1(float);
void Adjust();
void TD_PSOLA(float,float);
void PSOLA(float,float,bool);
unsigned GetNewLen();
short* GetNewData();
~CPsola();
private:
unsigned FindMax(unsigned,unsigned,short*);
int Approximate(float);
short Middle(unsigned,short*);
bool MarkPitch();
void MarkOneFrame(unsigned,unsigned);
void AdjustAmplitude();
void AdjustDuration();
void AdjustPitch();
void Smooth(short*,unsigned);
private:
unsigned m_uSamFre;
float m_dFrameLen;
float m_dX1;
float m_dAmpMul;
float m_dDuration;
unsigned m_uPitchLen;
float* m_dPitch;
float* m_dNewPitch;
unsigned m_uDataLen;
;
// unsigned m_uNewPitchLen;
short* m_Data;
bool* flag;
short* m_InData;
CDSP m_filter;
void GetPitchMarks(vector<unsigned>&);
bool IsVowel(unsigned);
int GetAvgPitchLen(vector<unsigned>&,int&);
void GetFinal(vector<unsigned>&,vector<unsigned>&,
int,vector<int>&,vector<vector<unsigned> >&);
void GetUseds(int,int,int,vector<int>&);
void smooth(short*,unsigned,vector<float>&);
void OverlapAdd(vector<vector<unsigned> >& final, short* y, unsigned ylen,
vector<float>& w, float* pBeta = NULL);
public:
void PSOLA(float,float*,int,float);
};
#endif
Subscribe to:
Posts (Atom)