media/libsoundtouch/src/SoundTouch.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 //////////////////////////////////////////////////////////////////////////////
     2 ///
     3 /// SoundTouch - main class for tempo/pitch/rate adjusting routines. 
     4 ///
     5 /// Notes:
     6 /// - Initialize the SoundTouch object instance by setting up the sound stream 
     7 ///   parameters with functions 'setSampleRate' and 'setChannels', then set 
     8 ///   desired tempo/pitch/rate settings with the corresponding functions.
     9 ///
    10 /// - The SoundTouch class behaves like a first-in-first-out pipeline: The 
    11 ///   samples that are to be processed are fed into one of the pipe by calling
    12 ///   function 'putSamples', while the ready processed samples can be read 
    13 ///   from the other end of the pipeline with function 'receiveSamples'.
    14 /// 
    15 /// - The SoundTouch processing classes require certain sized 'batches' of 
    16 ///   samples in order to process the sound. For this reason the classes buffer 
    17 ///   incoming samples until there are enough of samples available for 
    18 ///   processing, then they carry out the processing step and consequently
    19 ///   make the processed samples available for outputting.
    20 /// 
    21 /// - For the above reason, the processing routines introduce a certain 
    22 ///   'latency' between the input and output, so that the samples input to
    23 ///   SoundTouch may not be immediately available in the output, and neither 
    24 ///   the amount of outputtable samples may not immediately be in direct 
    25 ///   relationship with the amount of previously input samples.
    26 ///
    27 /// - The tempo/pitch/rate control parameters can be altered during processing.
    28 ///   Please notice though that they aren't currently protected by semaphores,
    29 ///   so in multi-thread application external semaphore protection may be
    30 ///   required.
    31 ///
    32 /// - This class utilizes classes 'TDStretch' for tempo change (without modifying
    33 ///   pitch) and 'RateTransposer' for changing the playback rate (that is, both 
    34 ///   tempo and pitch in the same ratio) of the sound. The third available control 
    35 ///   'pitch' (change pitch but maintain tempo) is produced by a combination of
    36 ///   combining the two other controls.
    37 ///
    38 /// Author        : Copyright (c) Olli Parviainen
    39 /// Author e-mail : oparviai 'at' iki.fi
    40 /// SoundTouch WWW: http://www.surina.net/soundtouch
    41 ///
    42 ////////////////////////////////////////////////////////////////////////////////
    43 //
    44 // Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
    45 // File revision : $Revision: 4 $
    46 //
    47 // $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $
    48 //
    49 ////////////////////////////////////////////////////////////////////////////////
    50 //
    51 // License :
    52 //
    53 //  SoundTouch audio processing library
    54 //  Copyright (c) Olli Parviainen
    55 //
    56 //  This library is free software; you can redistribute it and/or
    57 //  modify it under the terms of the GNU Lesser General Public
    58 //  License as published by the Free Software Foundation; either
    59 //  version 2.1 of the License, or (at your option) any later version.
    60 //
    61 //  This library is distributed in the hope that it will be useful,
    62 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    63 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    64 //  Lesser General Public License for more details.
    65 //
    66 //  You should have received a copy of the GNU Lesser General Public
    67 //  License along with this library; if not, write to the Free Software
    68 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    69 //
    70 ////////////////////////////////////////////////////////////////////////////////
    72 #include <assert.h>
    73 #include <stdlib.h>
    74 #include <memory.h>
    75 #include <math.h>
    76 #include <stdio.h>
    78 #include "SoundTouch.h"
    79 #include "TDStretch.h"
    80 #include "RateTransposer.h"
    81 #include "cpu_detect.h"
    83 #ifdef _MSC_VER
    84 #include <malloc.h>
    85 #define alloca _alloca
    86 #endif
    88 using namespace soundtouch;
    90 /// test if two floating point numbers are equal
    91 #define TEST_FLOAT_EQUAL(a, b)  (fabs(a - b) < 1e-10)
    94 /// Print library version string for autoconf
    95 extern "C" void soundtouch_ac_test()
    96 {
    97     printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
    98 } 
   101 SoundTouch::SoundTouch()
   102 {
   103     // Initialize rate transposer and tempo changer instances
   105     pRateTransposer = new RateTransposer();
   106     pTDStretch = TDStretch::newInstance();
   108     setOutPipe(pTDStretch);
   110     rate = tempo = 0;
   112     virtualPitch = 
   113     virtualRate = 
   114     virtualTempo = 1.0;
   116     calcEffectiveRateAndTempo();
   118     channels = 0;
   119     bSrateSet = false;
   120 }
   124 SoundTouch::~SoundTouch()
   125 {
   126     delete pRateTransposer;
   127     delete pTDStretch;
   128 }
   132 /// Get SoundTouch library version string
   133 const char *SoundTouch::getVersionString()
   134 {
   135     static const char *_version = SOUNDTOUCH_VERSION;
   137     return _version;
   138 }
   141 /// Get SoundTouch library version Id
   142 uint SoundTouch::getVersionId()
   143 {
   144     return SOUNDTOUCH_VERSION_ID;
   145 }
   148 // Sets the number of channels, 1 = mono, 2 = stereo
   149 void SoundTouch::setChannels(uint numChannels)
   150 {
   151     /*if (numChannels != 1 && numChannels != 2) 
   152     {
   153         //ST_THROW_RT_ERROR("Illegal number of channels");
   154 		return;
   155     }*/
   156     channels = numChannels;
   157     pRateTransposer->setChannels((int)numChannels);
   158     pTDStretch->setChannels((int)numChannels);
   159 }
   163 // Sets new rate control value. Normal rate = 1.0, smaller values
   164 // represent slower rate, larger faster rates.
   165 void SoundTouch::setRate(float newRate)
   166 {
   167     virtualRate = newRate;
   168     calcEffectiveRateAndTempo();
   169 }
   173 // Sets new rate control value as a difference in percents compared
   174 // to the original rate (-50 .. +100 %)
   175 void SoundTouch::setRateChange(float newRate)
   176 {
   177     virtualRate = 1.0f + 0.01f * newRate;
   178     calcEffectiveRateAndTempo();
   179 }
   183 // Sets new tempo control value. Normal tempo = 1.0, smaller values
   184 // represent slower tempo, larger faster tempo.
   185 void SoundTouch::setTempo(float newTempo)
   186 {
   187     virtualTempo = newTempo;
   188     calcEffectiveRateAndTempo();
   189 }
   193 // Sets new tempo control value as a difference in percents compared
   194 // to the original tempo (-50 .. +100 %)
   195 void SoundTouch::setTempoChange(float newTempo)
   196 {
   197     virtualTempo = 1.0f + 0.01f * newTempo;
   198     calcEffectiveRateAndTempo();
   199 }
   203 // Sets new pitch control value. Original pitch = 1.0, smaller values
   204 // represent lower pitches, larger values higher pitch.
   205 void SoundTouch::setPitch(float newPitch)
   206 {
   207     virtualPitch = newPitch;
   208     calcEffectiveRateAndTempo();
   209 }
   213 // Sets pitch change in octaves compared to the original pitch
   214 // (-1.00 .. +1.00)
   215 void SoundTouch::setPitchOctaves(float newPitch)
   216 {
   217     virtualPitch = (float)exp(0.69314718056f * newPitch);
   218     calcEffectiveRateAndTempo();
   219 }
   223 // Sets pitch change in semi-tones compared to the original pitch
   224 // (-12 .. +12)
   225 void SoundTouch::setPitchSemiTones(int newPitch)
   226 {
   227     setPitchOctaves((float)newPitch / 12.0f);
   228 }
   232 void SoundTouch::setPitchSemiTones(float newPitch)
   233 {
   234     setPitchOctaves(newPitch / 12.0f);
   235 }
   238 // Calculates 'effective' rate and tempo values from the
   239 // nominal control values.
   240 void SoundTouch::calcEffectiveRateAndTempo()
   241 {
   242     float oldTempo = tempo;
   243     float oldRate = rate;
   245     tempo = virtualTempo / virtualPitch;
   246     rate = virtualPitch * virtualRate;
   248     if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
   249     if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
   251 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
   252     if (rate <= 1.0f) 
   253     {
   254         if (output != pTDStretch) 
   255         {
   256             FIFOSamplePipe *tempoOut;
   258             assert(output == pRateTransposer);
   259             // move samples in the current output buffer to the output of pTDStretch
   260             tempoOut = pTDStretch->getOutput();
   261             tempoOut->moveSamples(*output);
   262             // move samples in pitch transposer's store buffer to tempo changer's input
   263             // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
   265             output = pTDStretch;
   266         }
   267     }
   268     else
   269 #endif
   270     {
   271         if (output != pRateTransposer) 
   272         {
   273             FIFOSamplePipe *transOut;
   275             assert(output == pTDStretch);
   276             // move samples in the current output buffer to the output of pRateTransposer
   277             transOut = pRateTransposer->getOutput();
   278             transOut->moveSamples(*output);
   279             // move samples in tempo changer's input to pitch transposer's input
   280             pRateTransposer->moveSamples(*pTDStretch->getInput());
   282             output = pRateTransposer;
   283         }
   284     } 
   285 }
   288 // Sets sample rate.
   289 void SoundTouch::setSampleRate(uint srate)
   290 {
   291     bSrateSet = true;
   292     // set sample rate, leave other tempo changer parameters as they are.
   293     pTDStretch->setParameters((int)srate);
   294 }
   297 // Adds 'numSamples' pcs of samples from the 'samples' memory position into
   298 // the input of the object.
   299 void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
   300 {
   301     if (bSrateSet == false) 
   302     {
   303         ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
   304     } 
   305     else if (channels == 0) 
   306     {
   307         ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
   308     }
   310     // Transpose the rate of the new samples if necessary
   311     /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
   312     if (rate == 1.0f) 
   313     {
   314         // The rate value is same as the original, simply evaluate the tempo changer. 
   315         assert(output == pTDStretch);
   316         if (pRateTransposer->isEmpty() == 0) 
   317         {
   318             // yet flush the last samples in the pitch transposer buffer
   319             // (may happen if 'rate' changes from a non-zero value to zero)
   320             pTDStretch->moveSamples(*pRateTransposer);
   321         }
   322         pTDStretch->putSamples(samples, nSamples);
   323     } 
   324     */
   325 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
   326     else if (rate <= 1.0f) 
   327     {
   328         // transpose the rate down, output the transposed sound to tempo changer buffer
   329         assert(output == pTDStretch);
   330         pRateTransposer->putSamples(samples, nSamples);
   331         pTDStretch->moveSamples(*pRateTransposer);
   332     } 
   333     else 
   334 #endif
   335     {
   336         // evaluate the tempo changer, then transpose the rate up, 
   337         assert(output == pRateTransposer);
   338         pTDStretch->putSamples(samples, nSamples);
   339         pRateTransposer->moveSamples(*pTDStretch);
   340     }
   341 }
   344 // Flushes the last samples from the processing pipeline to the output.
   345 // Clears also the internal processing buffers.
   346 //
   347 // Note: This function is meant for extracting the last samples of a sound
   348 // stream. This function may introduce additional blank samples in the end
   349 // of the sound stream, and thus it's not recommended to call this function
   350 // in the middle of a sound stream.
   351 void SoundTouch::flush()
   352 {
   353     int i;
   354     int nUnprocessed;
   355     int nOut;
   356     SAMPLETYPE *buff=(SAMPLETYPE*)alloca(64*channels*sizeof(SAMPLETYPE));
   358     // check how many samples still await processing, and scale
   359     // that by tempo & rate to get expected output sample count
   360     nUnprocessed = numUnprocessedSamples();
   361     nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
   363     nOut = numSamples();        // ready samples currently in buffer ...
   364     nOut += nUnprocessed;       // ... and how many we expect there to be in the end
   366     memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
   367     // "Push" the last active samples out from the processing pipeline by
   368     // feeding blank samples into the processing pipeline until new, 
   369     // processed samples appear in the output (not however, more than 
   370     // 8ksamples in any case)
   371     for (i = 0; i < 128; i ++) 
   372     {
   373         putSamples(buff, 64);
   374         if ((int)numSamples() >= nOut) 
   375         {
   376             // Enough new samples have appeared into the output!
   377             // As samples come from processing with bigger chunks, now truncate it
   378             // back to maximum "nOut" samples to improve duration accuracy 
   379             adjustAmountOfSamples(nOut);
   381             // finish
   382             break;  
   383         }
   384     }
   386     // Clear working buffers
   387     pRateTransposer->clear();
   388     pTDStretch->clearInput();
   389     // yet leave the 'tempoChanger' output intouched as that's where the
   390     // flushed samples are!
   391 }
   394 // Changes a setting controlling the processing system behaviour. See the
   395 // 'SETTING_...' defines for available setting ID's.
   396 bool SoundTouch::setSetting(int settingId, int value)
   397 {
   398     int sampleRate, sequenceMs, seekWindowMs, overlapMs;
   400     // read current tdstretch routine parameters
   401     pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
   403     switch (settingId) 
   404     {
   405         case SETTING_USE_AA_FILTER :
   406             // enables / disabless anti-alias filter
   407             pRateTransposer->enableAAFilter((value != 0) ? true : false);
   408             return true;
   410         case SETTING_AA_FILTER_LENGTH :
   411             // sets anti-alias filter length
   412             pRateTransposer->getAAFilter()->setLength(value);
   413             return true;
   415         case SETTING_USE_QUICKSEEK :
   416             // enables / disables tempo routine quick seeking algorithm
   417             pTDStretch->enableQuickSeek((value != 0) ? true : false);
   418             return true;
   420         case SETTING_SEQUENCE_MS:
   421             // change time-stretch sequence duration parameter
   422             pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
   423             return true;
   425         case SETTING_SEEKWINDOW_MS:
   426             // change time-stretch seek window length parameter
   427             pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
   428             return true;
   430         case SETTING_OVERLAP_MS:
   431             // change time-stretch overlap length parameter
   432             pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
   433             return true;
   435         default :
   436             return false;
   437     }
   438 }
   441 // Reads a setting controlling the processing system behaviour. See the
   442 // 'SETTING_...' defines for available setting ID's.
   443 //
   444 // Returns the setting value.
   445 int SoundTouch::getSetting(int settingId) const
   446 {
   447     int temp;
   449     switch (settingId) 
   450     {
   451         case SETTING_USE_AA_FILTER :
   452             return (uint)pRateTransposer->isAAFilterEnabled();
   454         case SETTING_AA_FILTER_LENGTH :
   455             return pRateTransposer->getAAFilter()->getLength();
   457         case SETTING_USE_QUICKSEEK :
   458             return (uint)   pTDStretch->isQuickSeekEnabled();
   460         case SETTING_SEQUENCE_MS:
   461             pTDStretch->getParameters(NULL, &temp, NULL, NULL);
   462             return temp;
   464         case SETTING_SEEKWINDOW_MS:
   465             pTDStretch->getParameters(NULL, NULL, &temp, NULL);
   466             return temp;
   468         case SETTING_OVERLAP_MS:
   469             pTDStretch->getParameters(NULL, NULL, NULL, &temp);
   470             return temp;
   472 		case SETTING_NOMINAL_INPUT_SEQUENCE :
   473 			return pTDStretch->getInputSampleReq();
   475 		case SETTING_NOMINAL_OUTPUT_SEQUENCE :
   476 			return pTDStretch->getOutputBatchSize();
   478 		default :
   479             return 0;
   480     }
   481 }
   484 // Clears all the samples in the object's output and internal processing
   485 // buffers.
   486 void SoundTouch::clear()
   487 {
   488     pRateTransposer->clear();
   489     pTDStretch->clear();
   490 }
   494 /// Returns number of samples currently unprocessed.
   495 uint SoundTouch::numUnprocessedSamples() const
   496 {
   497     FIFOSamplePipe * psp;
   498     if (pTDStretch)
   499     {
   500         psp = pTDStretch->getInput();
   501         if (psp)
   502         {
   503             return psp->numSamples();
   504         }
   505     }
   506     return 0;
   507 }

mercurial