mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| ////////////////////////////////////////////////////////////////////////////////
 | |
| /// 
 | |
| /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo 
 | |
| /// while maintaining the original pitch by using a time domain WSOLA-like method 
 | |
| /// with several performance-increasing tweaks.
 | |
| ///
 | |
| /// Note : MMX/SSE optimized functions reside in separate, platform-specific files 
 | |
| /// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
 | |
| ///
 | |
| /// Author        : Copyright (c) Olli Parviainen
 | |
| /// Author e-mail : oparviai 'at' iki.fi
 | |
| /// SoundTouch WWW: http://www.surina.net/soundtouch
 | |
| ///
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| // 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 TDStretch_H
 | |
| #define TDStretch_H
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include "STTypes.h"
 | |
| #include "RateTransposer.h"
 | |
| #include "FIFOSamplePipe.h"
 | |
| 
 | |
| namespace soundtouch
 | |
| {
 | |
| 
 | |
| /// Default values for sound processing parameters:
 | |
| /// Notice that the default parameters are tuned for contemporary popular music 
 | |
| /// processing. For speech processing applications these parameters suit better:
 | |
| ///     #define DEFAULT_SEQUENCE_MS     40
 | |
| ///     #define DEFAULT_SEEKWINDOW_MS   15
 | |
| ///     #define DEFAULT_OVERLAP_MS      8
 | |
| ///
 | |
| 
 | |
| /// Default length of a single processing sequence, in milliseconds. This determines to how 
 | |
| /// long sequences the original sound is chopped in the time-stretch algorithm.
 | |
| ///
 | |
| /// The larger this value is, the lesser sequences are used in processing. In principle
 | |
| /// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
 | |
| /// and vice versa.
 | |
| ///
 | |
| /// Increasing this value reduces computational burden & vice versa.
 | |
| //#define DEFAULT_SEQUENCE_MS         40
 | |
| #define DEFAULT_SEQUENCE_MS         USE_AUTO_SEQUENCE_LEN
 | |
| 
 | |
| /// Giving this value for the sequence length sets automatic parameter value
 | |
| /// according to tempo setting (recommended)
 | |
| #define USE_AUTO_SEQUENCE_LEN       0
 | |
| 
 | |
| /// Seeking window default length in milliseconds for algorithm that finds the best possible 
 | |
| /// overlapping location. This determines from how wide window the algorithm may look for an 
 | |
| /// optimal joining location when mixing the sound sequences back together. 
 | |
| ///
 | |
| /// The bigger this window setting is, the higher the possibility to find a better mixing
 | |
| /// position will become, but at the same time large values may cause a "drifting" artifact
 | |
| /// because consequent sequences will be taken at more uneven intervals.
 | |
| ///
 | |
| /// If there's a disturbing artifact that sounds as if a constant frequency was drifting 
 | |
| /// around, try reducing this setting.
 | |
| ///
 | |
| /// Increasing this value increases computational burden & vice versa.
 | |
| //#define DEFAULT_SEEKWINDOW_MS       15
 | |
| #define DEFAULT_SEEKWINDOW_MS       USE_AUTO_SEEKWINDOW_LEN
 | |
| 
 | |
| /// Giving this value for the seek window length sets automatic parameter value
 | |
| /// according to tempo setting (recommended)
 | |
| #define USE_AUTO_SEEKWINDOW_LEN     0
 | |
| 
 | |
| /// Overlap length in milliseconds. When the chopped sound sequences are mixed back together, 
 | |
| /// to form a continuous sound stream, this parameter defines over how long period the two 
 | |
| /// consecutive sequences are let to overlap each other. 
 | |
| ///
 | |
| /// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting 
 | |
| /// by a large amount, you might wish to try a smaller value on this.
 | |
| ///
 | |
| /// Increasing this value increases computational burden & vice versa.
 | |
| #define DEFAULT_OVERLAP_MS      8
 | |
| 
 | |
| 
 | |
| /// Class that does the time-stretch (tempo change) effect for the processed
 | |
| /// sound.
 | |
| class TDStretch : public FIFOProcessor
 | |
| {
 | |
| protected:
 | |
|     int channels;
 | |
|     int sampleReq;
 | |
| 
 | |
|     int overlapLength;
 | |
|     int seekLength;
 | |
|     int seekWindowLength;
 | |
|     int overlapDividerBitsNorm;
 | |
|     int overlapDividerBitsPure;
 | |
|     int slopingDivider;
 | |
|     int sampleRate;
 | |
|     int sequenceMs;
 | |
|     int seekWindowMs;
 | |
|     int overlapMs;
 | |
| 
 | |
|     unsigned long maxnorm;
 | |
|     float maxnormf;
 | |
| 
 | |
|     double tempo;
 | |
|     double nominalSkip;
 | |
|     double skipFract;
 | |
| 
 | |
|     bool bQuickSeek;
 | |
|     bool bAutoSeqSetting;
 | |
|     bool bAutoSeekSetting;
 | |
|     bool isBeginning;
 | |
| 
 | |
|     SAMPLETYPE *pMidBuffer;
 | |
|     SAMPLETYPE *pMidBufferUnaligned;
 | |
| 
 | |
|     FIFOSampleBuffer outputBuffer;
 | |
|     FIFOSampleBuffer inputBuffer;
 | |
| 
 | |
|     void acceptNewOverlapLength(int newOverlapLength);
 | |
| 
 | |
|     virtual void clearCrossCorrState();
 | |
|     void calculateOverlapLength(int overlapMs);
 | |
| 
 | |
|     virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
 | |
|     virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
 | |
| 
 | |
|     virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
 | |
|     virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
 | |
|     virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
 | |
| 
 | |
|     virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
 | |
|     virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
 | |
|     virtual void overlapMulti(SAMPLETYPE *output, const SAMPLETYPE *input) const;
 | |
| 
 | |
|     void clearMidBuffer();
 | |
|     void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
 | |
| 
 | |
|     void calcSeqParameters();
 | |
|     void adaptNormalizer();
 | |
| 
 | |
|     /// Changes the tempo of the given sound samples.
 | |
|     /// Returns amount of samples returned in the "output" buffer.
 | |
|     /// The maximum amount of samples that can be returned at a time is set by
 | |
|     /// the 'set_returnBuffer_size' function.
 | |
|     void processSamples();
 | |
|     
 | |
| public:
 | |
|     TDStretch();
 | |
|     virtual ~TDStretch() override;
 | |
| 
 | |
|     /// Operator 'new' is overloaded so that it automatically creates a suitable instance 
 | |
|     /// depending on if we've a MMX/SSE/etc-capable CPU available or not.
 | |
|     static void *operator new(size_t s);
 | |
| 
 | |
|     /// Use this function instead of "new" operator to create a new instance of this class. 
 | |
|     /// This function automatically chooses a correct feature set depending on if the CPU
 | |
|     /// supports MMX/SSE/etc extensions.
 | |
|     static TDStretch *newInstance();
 | |
|     
 | |
|     /// Returns the output buffer object
 | |
|     FIFOSamplePipe *getOutput() { return &outputBuffer; };
 | |
| 
 | |
|     /// Returns the input buffer object
 | |
|     FIFOSamplePipe *getInput() { return &inputBuffer; };
 | |
| 
 | |
|     /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower 
 | |
|     /// tempo, larger faster tempo.
 | |
|     void setTempo(double newTempo);
 | |
| 
 | |
|     /// Returns nonzero if there aren't any samples available for outputting.
 | |
|     virtual void clear() override;
 | |
| 
 | |
|     /// Clears the input buffer
 | |
|     void clearInput();
 | |
| 
 | |
|     /// Sets the number of channels, 1 = mono, 2 = stereo
 | |
|     void setChannels(int numChannels);
 | |
| 
 | |
|     /// Enables/disables the quick position seeking algorithm. Zero to disable, 
 | |
|     /// nonzero to enable
 | |
|     void enableQuickSeek(bool enable);
 | |
| 
 | |
|     /// Returns nonzero if the quick seeking algorithm is enabled.
 | |
|     bool isQuickSeekEnabled() const;
 | |
| 
 | |
|     /// Sets routine control parameters. These control are certain time constants
 | |
|     /// defining how the sound is stretched to the desired duration.
 | |
|     //
 | |
|     /// 'sampleRate' = sample rate of the sound
 | |
|     /// 'sequenceMS' = one processing sequence length in milliseconds
 | |
|     /// 'seekwindowMS' = seeking window length for scanning the best overlapping 
 | |
|     ///      position
 | |
|     /// 'overlapMS' = overlapping length
 | |
|     void setParameters(int sampleRate,          ///< Samplerate of sound being processed (Hz)
 | |
|                        int sequenceMS = -1,     ///< Single processing sequence length (ms)
 | |
|                        int seekwindowMS = -1,   ///< Offset seeking window length (ms)
 | |
|                        int overlapMS = -1       ///< Sequence overlapping length (ms)
 | |
|                        );
 | |
| 
 | |
|     /// Get routine control parameters, see setParameters() function.
 | |
|     /// Any of the parameters to this function can be NULL, in such case corresponding parameter
 | |
|     /// value isn't returned.
 | |
|     void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
 | |
| 
 | |
|     /// Adds 'numsamples' pcs of samples from the 'samples' memory position into
 | |
|     /// the input of the object.
 | |
|     virtual void putSamples(
 | |
|             const SAMPLETYPE *samples,  ///< Input sample data
 | |
|             uint numSamples                         ///< Number of samples in 'samples' so that one sample
 | |
|                                                     ///< contains both channels if stereo
 | |
|             ) override;
 | |
| 
 | |
|     /// return nominal input sample requirement for triggering a processing batch
 | |
|     int getInputSampleReq() const
 | |
|     {
 | |
|         return (int)(nominalSkip + 0.5);
 | |
|     }
 | |
| 
 | |
|     /// return nominal output sample amount when running a processing batch
 | |
|     int getOutputBatchSize() const
 | |
|     {
 | |
|         return seekWindowLength - overlapLength;
 | |
|     }
 | |
| 
 | |
| 	/// return approximate initial input-output latency
 | |
| 	int getLatency() const
 | |
| 	{
 | |
| 		return sampleReq;
 | |
| 	}
 | |
| };
 | |
| 
 | |
| 
 | |
| // Implementation-specific class declarations:
 | |
| 
 | |
| #ifdef SOUNDTOUCH_ALLOW_MMX
 | |
|     /// Class that implements MMX optimized routines for 16bit integer samples type.
 | |
|     class TDStretchMMX : public TDStretch
 | |
|     {
 | |
|     protected:
 | |
|         double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) override;
 | |
|         double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) override;
 | |
|         virtual void overlapStereo(short *output, const short *input) const override;
 | |
|         virtual void clearCrossCorrState() override;
 | |
|     };
 | |
| #endif /// SOUNDTOUCH_ALLOW_MMX
 | |
| 
 | |
| 
 | |
| #ifdef SOUNDTOUCH_ALLOW_SSE
 | |
|     /// Class that implements SSE optimized routines for floating point samples type.
 | |
|     class TDStretchSSE : public TDStretch
 | |
|     {
 | |
|     protected:
 | |
|         double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) override;
 | |
|         double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) override;
 | |
|     };
 | |
| 
 | |
| #endif /// SOUNDTOUCH_ALLOW_SSE
 | |
| 
 | |
| }
 | |
| #endif  /// TDStretch_H
 | 
