michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: /// michael@0: /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo michael@0: /// while maintaining the original pitch by using a time domain WSOLA-like method michael@0: /// with several performance-increasing tweaks. michael@0: /// michael@0: /// Note : MMX/SSE optimized functions reside in separate, platform-specific files michael@0: /// 'mmx_optimized.cpp' and 'sse_optimized.cpp' michael@0: /// michael@0: /// Author : Copyright (c) Olli Parviainen michael@0: /// Author e-mail : oparviai 'at' iki.fi michael@0: /// SoundTouch WWW: http://www.surina.net/soundtouch michael@0: /// michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // Last changed : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $ michael@0: // File revision : $Revision: 4 $ michael@0: // michael@0: // $Id: TDStretch.h 195 2014-04-06 15:57:21Z oparviai $ michael@0: // michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // License : michael@0: // michael@0: // SoundTouch audio processing library michael@0: // Copyright (c) Olli Parviainen michael@0: // michael@0: // This library is free software; you can redistribute it and/or michael@0: // modify it under the terms of the GNU Lesser General Public michael@0: // License as published by the Free Software Foundation; either michael@0: // version 2.1 of the License, or (at your option) any later version. michael@0: // michael@0: // This library is distributed in the hope that it will be useful, michael@0: // but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: // Lesser General Public License for more details. michael@0: // michael@0: // You should have received a copy of the GNU Lesser General Public michael@0: // License along with this library; if not, write to the Free Software michael@0: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA michael@0: // michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #ifndef TDStretch_H michael@0: #define TDStretch_H michael@0: michael@0: #include michael@0: #include "STTypes.h" michael@0: #include "RateTransposer.h" michael@0: #include "FIFOSamplePipe.h" michael@0: michael@0: namespace soundtouch michael@0: { michael@0: michael@0: /// Default values for sound processing parameters: michael@0: /// Notice that the default parameters are tuned for contemporary popular music michael@0: /// processing. For speech processing applications these parameters suit better: michael@0: /// #define DEFAULT_SEQUENCE_MS 40 michael@0: /// #define DEFAULT_SEEKWINDOW_MS 15 michael@0: /// #define DEFAULT_OVERLAP_MS 8 michael@0: /// michael@0: michael@0: /// Default length of a single processing sequence, in milliseconds. This determines to how michael@0: /// long sequences the original sound is chopped in the time-stretch algorithm. michael@0: /// michael@0: /// The larger this value is, the lesser sequences are used in processing. In principle michael@0: /// a bigger value sounds better when slowing down tempo, but worse when increasing tempo michael@0: /// and vice versa. michael@0: /// michael@0: /// Increasing this value reduces computational burden & vice versa. michael@0: //#define DEFAULT_SEQUENCE_MS 40 michael@0: #define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN michael@0: michael@0: /// Giving this value for the sequence length sets automatic parameter value michael@0: /// according to tempo setting (recommended) michael@0: #define USE_AUTO_SEQUENCE_LEN 0 michael@0: michael@0: /// Seeking window default length in milliseconds for algorithm that finds the best possible michael@0: /// overlapping location. This determines from how wide window the algorithm may look for an michael@0: /// optimal joining location when mixing the sound sequences back together. michael@0: /// michael@0: /// The bigger this window setting is, the higher the possibility to find a better mixing michael@0: /// position will become, but at the same time large values may cause a "drifting" artifact michael@0: /// because consequent sequences will be taken at more uneven intervals. michael@0: /// michael@0: /// If there's a disturbing artifact that sounds as if a constant frequency was drifting michael@0: /// around, try reducing this setting. michael@0: /// michael@0: /// Increasing this value increases computational burden & vice versa. michael@0: //#define DEFAULT_SEEKWINDOW_MS 15 michael@0: #define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN michael@0: michael@0: /// Giving this value for the seek window length sets automatic parameter value michael@0: /// according to tempo setting (recommended) michael@0: #define USE_AUTO_SEEKWINDOW_LEN 0 michael@0: michael@0: /// Overlap length in milliseconds. When the chopped sound sequences are mixed back together, michael@0: /// to form a continuous sound stream, this parameter defines over how long period the two michael@0: /// consecutive sequences are let to overlap each other. michael@0: /// michael@0: /// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting michael@0: /// by a large amount, you might wish to try a smaller value on this. michael@0: /// michael@0: /// Increasing this value increases computational burden & vice versa. michael@0: #define DEFAULT_OVERLAP_MS 8 michael@0: michael@0: michael@0: /// Class that does the time-stretch (tempo change) effect for the processed michael@0: /// sound. michael@0: class TDStretch : public FIFOProcessor michael@0: { michael@0: protected: michael@0: int channels; michael@0: int sampleReq; michael@0: float tempo; michael@0: michael@0: SAMPLETYPE *pMidBuffer; michael@0: SAMPLETYPE *pMidBufferUnaligned; michael@0: int overlapLength; michael@0: int seekLength; michael@0: int seekWindowLength; michael@0: int overlapDividerBits; michael@0: int slopingDivider; michael@0: float nominalSkip; michael@0: float skipFract; michael@0: FIFOSampleBuffer outputBuffer; michael@0: FIFOSampleBuffer inputBuffer; michael@0: bool bQuickSeek; michael@0: michael@0: int sampleRate; michael@0: int sequenceMs; michael@0: int seekWindowMs; michael@0: int overlapMs; michael@0: bool bAutoSeqSetting; michael@0: bool bAutoSeekSetting; michael@0: michael@0: void acceptNewOverlapLength(int newOverlapLength); michael@0: michael@0: virtual void clearCrossCorrState(); michael@0: void calculateOverlapLength(int overlapMs); michael@0: michael@0: virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const; michael@0: virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const; michael@0: michael@0: virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos); michael@0: virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos); michael@0: int seekBestOverlapPosition(const SAMPLETYPE *refPos); michael@0: michael@0: virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const; michael@0: virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const; michael@0: virtual void overlapMulti(SAMPLETYPE *output, const SAMPLETYPE *input) const; michael@0: michael@0: void clearMidBuffer(); michael@0: void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const; michael@0: michael@0: void calcSeqParameters(); michael@0: michael@0: /// Changes the tempo of the given sound samples. michael@0: /// Returns amount of samples returned in the "output" buffer. michael@0: /// The maximum amount of samples that can be returned at a time is set by michael@0: /// the 'set_returnBuffer_size' function. michael@0: void processSamples(); michael@0: michael@0: public: michael@0: TDStretch(); michael@0: virtual ~TDStretch(); michael@0: michael@0: /// Operator 'new' is overloaded so that it automatically creates a suitable instance michael@0: /// depending on if we've a MMX/SSE/etc-capable CPU available or not. michael@0: static void *operator new(size_t s); michael@0: michael@0: /// Use this function instead of "new" operator to create a new instance of this class. michael@0: /// This function automatically chooses a correct feature set depending on if the CPU michael@0: /// supports MMX/SSE/etc extensions. michael@0: static TDStretch *newInstance(); michael@0: michael@0: /// Returns the output buffer object michael@0: FIFOSamplePipe *getOutput() { return &outputBuffer; }; michael@0: michael@0: /// Returns the input buffer object michael@0: FIFOSamplePipe *getInput() { return &inputBuffer; }; michael@0: michael@0: /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower michael@0: /// tempo, larger faster tempo. michael@0: void setTempo(float newTempo); michael@0: michael@0: /// Returns nonzero if there aren't any samples available for outputting. michael@0: virtual void clear(); michael@0: michael@0: /// Clears the input buffer michael@0: void clearInput(); michael@0: michael@0: /// Sets the number of channels, 1 = mono, 2 = stereo michael@0: void setChannels(int numChannels); michael@0: michael@0: /// Enables/disables the quick position seeking algorithm. Zero to disable, michael@0: /// nonzero to enable michael@0: void enableQuickSeek(bool enable); michael@0: michael@0: /// Returns nonzero if the quick seeking algorithm is enabled. michael@0: bool isQuickSeekEnabled() const; michael@0: michael@0: /// Sets routine control parameters. These control are certain time constants michael@0: /// defining how the sound is stretched to the desired duration. michael@0: // michael@0: /// 'sampleRate' = sample rate of the sound michael@0: /// 'sequenceMS' = one processing sequence length in milliseconds michael@0: /// 'seekwindowMS' = seeking window length for scanning the best overlapping michael@0: /// position michael@0: /// 'overlapMS' = overlapping length michael@0: void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz) michael@0: int sequenceMS = -1, ///< Single processing sequence length (ms) michael@0: int seekwindowMS = -1, ///< Offset seeking window length (ms) michael@0: int overlapMS = -1 ///< Sequence overlapping length (ms) michael@0: ); michael@0: michael@0: /// Get routine control parameters, see setParameters() function. michael@0: /// Any of the parameters to this function can be NULL, in such case corresponding parameter michael@0: /// value isn't returned. michael@0: void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const; michael@0: michael@0: /// Adds 'numsamples' pcs of samples from the 'samples' memory position into michael@0: /// the input of the object. michael@0: virtual void putSamples( michael@0: const SAMPLETYPE *samples, ///< Input sample data michael@0: uint numSamples ///< Number of samples in 'samples' so that one sample michael@0: ///< contains both channels if stereo michael@0: ); michael@0: michael@0: /// return nominal input sample requirement for triggering a processing batch michael@0: int getInputSampleReq() const michael@0: { michael@0: return (int)(nominalSkip + 0.5); michael@0: } michael@0: michael@0: /// return nominal output sample amount when running a processing batch michael@0: int getOutputBatchSize() const michael@0: { michael@0: return seekWindowLength - overlapLength; michael@0: } michael@0: }; michael@0: michael@0: michael@0: michael@0: // Implementation-specific class declarations: michael@0: michael@0: #ifdef SOUNDTOUCH_ALLOW_MMX michael@0: /// Class that implements MMX optimized routines for 16bit integer samples type. michael@0: class TDStretchMMX : public TDStretch michael@0: { michael@0: protected: michael@0: double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const; michael@0: double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const; michael@0: virtual void overlapStereo(short *output, const short *input) const; michael@0: virtual void clearCrossCorrState(); michael@0: }; michael@0: #endif /// SOUNDTOUCH_ALLOW_MMX michael@0: michael@0: michael@0: #ifdef SOUNDTOUCH_ALLOW_SSE michael@0: /// Class that implements SSE optimized routines for floating point samples type. michael@0: class TDStretchSSE : public TDStretch michael@0: { michael@0: protected: michael@0: double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const; michael@0: double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const; michael@0: }; michael@0: michael@0: #endif /// SOUNDTOUCH_ALLOW_SSE michael@0: michael@0: } michael@0: #endif /// TDStretch_H