media/libsoundtouch/src/SoundTouch.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/media/libsoundtouch/src/SoundTouch.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,507 @@
     1.4 +//////////////////////////////////////////////////////////////////////////////
     1.5 +///
     1.6 +/// SoundTouch - main class for tempo/pitch/rate adjusting routines. 
     1.7 +///
     1.8 +/// Notes:
     1.9 +/// - Initialize the SoundTouch object instance by setting up the sound stream 
    1.10 +///   parameters with functions 'setSampleRate' and 'setChannels', then set 
    1.11 +///   desired tempo/pitch/rate settings with the corresponding functions.
    1.12 +///
    1.13 +/// - The SoundTouch class behaves like a first-in-first-out pipeline: The 
    1.14 +///   samples that are to be processed are fed into one of the pipe by calling
    1.15 +///   function 'putSamples', while the ready processed samples can be read 
    1.16 +///   from the other end of the pipeline with function 'receiveSamples'.
    1.17 +/// 
    1.18 +/// - The SoundTouch processing classes require certain sized 'batches' of 
    1.19 +///   samples in order to process the sound. For this reason the classes buffer 
    1.20 +///   incoming samples until there are enough of samples available for 
    1.21 +///   processing, then they carry out the processing step and consequently
    1.22 +///   make the processed samples available for outputting.
    1.23 +/// 
    1.24 +/// - For the above reason, the processing routines introduce a certain 
    1.25 +///   'latency' between the input and output, so that the samples input to
    1.26 +///   SoundTouch may not be immediately available in the output, and neither 
    1.27 +///   the amount of outputtable samples may not immediately be in direct 
    1.28 +///   relationship with the amount of previously input samples.
    1.29 +///
    1.30 +/// - The tempo/pitch/rate control parameters can be altered during processing.
    1.31 +///   Please notice though that they aren't currently protected by semaphores,
    1.32 +///   so in multi-thread application external semaphore protection may be
    1.33 +///   required.
    1.34 +///
    1.35 +/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
    1.36 +///   pitch) and 'RateTransposer' for changing the playback rate (that is, both 
    1.37 +///   tempo and pitch in the same ratio) of the sound. The third available control 
    1.38 +///   'pitch' (change pitch but maintain tempo) is produced by a combination of
    1.39 +///   combining the two other controls.
    1.40 +///
    1.41 +/// Author        : Copyright (c) Olli Parviainen
    1.42 +/// Author e-mail : oparviai 'at' iki.fi
    1.43 +/// SoundTouch WWW: http://www.surina.net/soundtouch
    1.44 +///
    1.45 +////////////////////////////////////////////////////////////////////////////////
    1.46 +//
    1.47 +// Last changed  : $Date: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $
    1.48 +// File revision : $Revision: 4 $
    1.49 +//
    1.50 +// $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $
    1.51 +//
    1.52 +////////////////////////////////////////////////////////////////////////////////
    1.53 +//
    1.54 +// License :
    1.55 +//
    1.56 +//  SoundTouch audio processing library
    1.57 +//  Copyright (c) Olli Parviainen
    1.58 +//
    1.59 +//  This library is free software; you can redistribute it and/or
    1.60 +//  modify it under the terms of the GNU Lesser General Public
    1.61 +//  License as published by the Free Software Foundation; either
    1.62 +//  version 2.1 of the License, or (at your option) any later version.
    1.63 +//
    1.64 +//  This library is distributed in the hope that it will be useful,
    1.65 +//  but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.66 +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.67 +//  Lesser General Public License for more details.
    1.68 +//
    1.69 +//  You should have received a copy of the GNU Lesser General Public
    1.70 +//  License along with this library; if not, write to the Free Software
    1.71 +//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.72 +//
    1.73 +////////////////////////////////////////////////////////////////////////////////
    1.74 +
    1.75 +#include <assert.h>
    1.76 +#include <stdlib.h>
    1.77 +#include <memory.h>
    1.78 +#include <math.h>
    1.79 +#include <stdio.h>
    1.80 +
    1.81 +#include "SoundTouch.h"
    1.82 +#include "TDStretch.h"
    1.83 +#include "RateTransposer.h"
    1.84 +#include "cpu_detect.h"
    1.85 +
    1.86 +#ifdef _MSC_VER
    1.87 +#include <malloc.h>
    1.88 +#define alloca _alloca
    1.89 +#endif
    1.90 +
    1.91 +using namespace soundtouch;
    1.92 +    
    1.93 +/// test if two floating point numbers are equal
    1.94 +#define TEST_FLOAT_EQUAL(a, b)  (fabs(a - b) < 1e-10)
    1.95 +
    1.96 +
    1.97 +/// Print library version string for autoconf
    1.98 +extern "C" void soundtouch_ac_test()
    1.99 +{
   1.100 +    printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
   1.101 +} 
   1.102 +
   1.103 +
   1.104 +SoundTouch::SoundTouch()
   1.105 +{
   1.106 +    // Initialize rate transposer and tempo changer instances
   1.107 +
   1.108 +    pRateTransposer = new RateTransposer();
   1.109 +    pTDStretch = TDStretch::newInstance();
   1.110 +
   1.111 +    setOutPipe(pTDStretch);
   1.112 +
   1.113 +    rate = tempo = 0;
   1.114 +
   1.115 +    virtualPitch = 
   1.116 +    virtualRate = 
   1.117 +    virtualTempo = 1.0;
   1.118 +
   1.119 +    calcEffectiveRateAndTempo();
   1.120 +
   1.121 +    channels = 0;
   1.122 +    bSrateSet = false;
   1.123 +}
   1.124 +
   1.125 +
   1.126 +
   1.127 +SoundTouch::~SoundTouch()
   1.128 +{
   1.129 +    delete pRateTransposer;
   1.130 +    delete pTDStretch;
   1.131 +}
   1.132 +
   1.133 +
   1.134 +
   1.135 +/// Get SoundTouch library version string
   1.136 +const char *SoundTouch::getVersionString()
   1.137 +{
   1.138 +    static const char *_version = SOUNDTOUCH_VERSION;
   1.139 +
   1.140 +    return _version;
   1.141 +}
   1.142 +
   1.143 +
   1.144 +/// Get SoundTouch library version Id
   1.145 +uint SoundTouch::getVersionId()
   1.146 +{
   1.147 +    return SOUNDTOUCH_VERSION_ID;
   1.148 +}
   1.149 +
   1.150 +
   1.151 +// Sets the number of channels, 1 = mono, 2 = stereo
   1.152 +void SoundTouch::setChannels(uint numChannels)
   1.153 +{
   1.154 +    /*if (numChannels != 1 && numChannels != 2) 
   1.155 +    {
   1.156 +        //ST_THROW_RT_ERROR("Illegal number of channels");
   1.157 +		return;
   1.158 +    }*/
   1.159 +    channels = numChannels;
   1.160 +    pRateTransposer->setChannels((int)numChannels);
   1.161 +    pTDStretch->setChannels((int)numChannels);
   1.162 +}
   1.163 +
   1.164 +
   1.165 +
   1.166 +// Sets new rate control value. Normal rate = 1.0, smaller values
   1.167 +// represent slower rate, larger faster rates.
   1.168 +void SoundTouch::setRate(float newRate)
   1.169 +{
   1.170 +    virtualRate = newRate;
   1.171 +    calcEffectiveRateAndTempo();
   1.172 +}
   1.173 +
   1.174 +
   1.175 +
   1.176 +// Sets new rate control value as a difference in percents compared
   1.177 +// to the original rate (-50 .. +100 %)
   1.178 +void SoundTouch::setRateChange(float newRate)
   1.179 +{
   1.180 +    virtualRate = 1.0f + 0.01f * newRate;
   1.181 +    calcEffectiveRateAndTempo();
   1.182 +}
   1.183 +
   1.184 +
   1.185 +
   1.186 +// Sets new tempo control value. Normal tempo = 1.0, smaller values
   1.187 +// represent slower tempo, larger faster tempo.
   1.188 +void SoundTouch::setTempo(float newTempo)
   1.189 +{
   1.190 +    virtualTempo = newTempo;
   1.191 +    calcEffectiveRateAndTempo();
   1.192 +}
   1.193 +
   1.194 +
   1.195 +
   1.196 +// Sets new tempo control value as a difference in percents compared
   1.197 +// to the original tempo (-50 .. +100 %)
   1.198 +void SoundTouch::setTempoChange(float newTempo)
   1.199 +{
   1.200 +    virtualTempo = 1.0f + 0.01f * newTempo;
   1.201 +    calcEffectiveRateAndTempo();
   1.202 +}
   1.203 +
   1.204 +
   1.205 +
   1.206 +// Sets new pitch control value. Original pitch = 1.0, smaller values
   1.207 +// represent lower pitches, larger values higher pitch.
   1.208 +void SoundTouch::setPitch(float newPitch)
   1.209 +{
   1.210 +    virtualPitch = newPitch;
   1.211 +    calcEffectiveRateAndTempo();
   1.212 +}
   1.213 +
   1.214 +
   1.215 +
   1.216 +// Sets pitch change in octaves compared to the original pitch
   1.217 +// (-1.00 .. +1.00)
   1.218 +void SoundTouch::setPitchOctaves(float newPitch)
   1.219 +{
   1.220 +    virtualPitch = (float)exp(0.69314718056f * newPitch);
   1.221 +    calcEffectiveRateAndTempo();
   1.222 +}
   1.223 +
   1.224 +
   1.225 +
   1.226 +// Sets pitch change in semi-tones compared to the original pitch
   1.227 +// (-12 .. +12)
   1.228 +void SoundTouch::setPitchSemiTones(int newPitch)
   1.229 +{
   1.230 +    setPitchOctaves((float)newPitch / 12.0f);
   1.231 +}
   1.232 +
   1.233 +
   1.234 +
   1.235 +void SoundTouch::setPitchSemiTones(float newPitch)
   1.236 +{
   1.237 +    setPitchOctaves(newPitch / 12.0f);
   1.238 +}
   1.239 +
   1.240 +
   1.241 +// Calculates 'effective' rate and tempo values from the
   1.242 +// nominal control values.
   1.243 +void SoundTouch::calcEffectiveRateAndTempo()
   1.244 +{
   1.245 +    float oldTempo = tempo;
   1.246 +    float oldRate = rate;
   1.247 +
   1.248 +    tempo = virtualTempo / virtualPitch;
   1.249 +    rate = virtualPitch * virtualRate;
   1.250 +
   1.251 +    if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
   1.252 +    if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
   1.253 +
   1.254 +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
   1.255 +    if (rate <= 1.0f) 
   1.256 +    {
   1.257 +        if (output != pTDStretch) 
   1.258 +        {
   1.259 +            FIFOSamplePipe *tempoOut;
   1.260 +
   1.261 +            assert(output == pRateTransposer);
   1.262 +            // move samples in the current output buffer to the output of pTDStretch
   1.263 +            tempoOut = pTDStretch->getOutput();
   1.264 +            tempoOut->moveSamples(*output);
   1.265 +            // move samples in pitch transposer's store buffer to tempo changer's input
   1.266 +            // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
   1.267 +
   1.268 +            output = pTDStretch;
   1.269 +        }
   1.270 +    }
   1.271 +    else
   1.272 +#endif
   1.273 +    {
   1.274 +        if (output != pRateTransposer) 
   1.275 +        {
   1.276 +            FIFOSamplePipe *transOut;
   1.277 +
   1.278 +            assert(output == pTDStretch);
   1.279 +            // move samples in the current output buffer to the output of pRateTransposer
   1.280 +            transOut = pRateTransposer->getOutput();
   1.281 +            transOut->moveSamples(*output);
   1.282 +            // move samples in tempo changer's input to pitch transposer's input
   1.283 +            pRateTransposer->moveSamples(*pTDStretch->getInput());
   1.284 +
   1.285 +            output = pRateTransposer;
   1.286 +        }
   1.287 +    } 
   1.288 +}
   1.289 +
   1.290 +
   1.291 +// Sets sample rate.
   1.292 +void SoundTouch::setSampleRate(uint srate)
   1.293 +{
   1.294 +    bSrateSet = true;
   1.295 +    // set sample rate, leave other tempo changer parameters as they are.
   1.296 +    pTDStretch->setParameters((int)srate);
   1.297 +}
   1.298 +
   1.299 +
   1.300 +// Adds 'numSamples' pcs of samples from the 'samples' memory position into
   1.301 +// the input of the object.
   1.302 +void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
   1.303 +{
   1.304 +    if (bSrateSet == false) 
   1.305 +    {
   1.306 +        ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
   1.307 +    } 
   1.308 +    else if (channels == 0) 
   1.309 +    {
   1.310 +        ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
   1.311 +    }
   1.312 +
   1.313 +    // Transpose the rate of the new samples if necessary
   1.314 +    /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
   1.315 +    if (rate == 1.0f) 
   1.316 +    {
   1.317 +        // The rate value is same as the original, simply evaluate the tempo changer. 
   1.318 +        assert(output == pTDStretch);
   1.319 +        if (pRateTransposer->isEmpty() == 0) 
   1.320 +        {
   1.321 +            // yet flush the last samples in the pitch transposer buffer
   1.322 +            // (may happen if 'rate' changes from a non-zero value to zero)
   1.323 +            pTDStretch->moveSamples(*pRateTransposer);
   1.324 +        }
   1.325 +        pTDStretch->putSamples(samples, nSamples);
   1.326 +    } 
   1.327 +    */
   1.328 +#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
   1.329 +    else if (rate <= 1.0f) 
   1.330 +    {
   1.331 +        // transpose the rate down, output the transposed sound to tempo changer buffer
   1.332 +        assert(output == pTDStretch);
   1.333 +        pRateTransposer->putSamples(samples, nSamples);
   1.334 +        pTDStretch->moveSamples(*pRateTransposer);
   1.335 +    } 
   1.336 +    else 
   1.337 +#endif
   1.338 +    {
   1.339 +        // evaluate the tempo changer, then transpose the rate up, 
   1.340 +        assert(output == pRateTransposer);
   1.341 +        pTDStretch->putSamples(samples, nSamples);
   1.342 +        pRateTransposer->moveSamples(*pTDStretch);
   1.343 +    }
   1.344 +}
   1.345 +
   1.346 +
   1.347 +// Flushes the last samples from the processing pipeline to the output.
   1.348 +// Clears also the internal processing buffers.
   1.349 +//
   1.350 +// Note: This function is meant for extracting the last samples of a sound
   1.351 +// stream. This function may introduce additional blank samples in the end
   1.352 +// of the sound stream, and thus it's not recommended to call this function
   1.353 +// in the middle of a sound stream.
   1.354 +void SoundTouch::flush()
   1.355 +{
   1.356 +    int i;
   1.357 +    int nUnprocessed;
   1.358 +    int nOut;
   1.359 +    SAMPLETYPE *buff=(SAMPLETYPE*)alloca(64*channels*sizeof(SAMPLETYPE));
   1.360 +
   1.361 +    // check how many samples still await processing, and scale
   1.362 +    // that by tempo & rate to get expected output sample count
   1.363 +    nUnprocessed = numUnprocessedSamples();
   1.364 +    nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
   1.365 +
   1.366 +    nOut = numSamples();        // ready samples currently in buffer ...
   1.367 +    nOut += nUnprocessed;       // ... and how many we expect there to be in the end
   1.368 +    
   1.369 +    memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
   1.370 +    // "Push" the last active samples out from the processing pipeline by
   1.371 +    // feeding blank samples into the processing pipeline until new, 
   1.372 +    // processed samples appear in the output (not however, more than 
   1.373 +    // 8ksamples in any case)
   1.374 +    for (i = 0; i < 128; i ++) 
   1.375 +    {
   1.376 +        putSamples(buff, 64);
   1.377 +        if ((int)numSamples() >= nOut) 
   1.378 +        {
   1.379 +            // Enough new samples have appeared into the output!
   1.380 +            // As samples come from processing with bigger chunks, now truncate it
   1.381 +            // back to maximum "nOut" samples to improve duration accuracy 
   1.382 +            adjustAmountOfSamples(nOut);
   1.383 +
   1.384 +            // finish
   1.385 +            break;  
   1.386 +        }
   1.387 +    }
   1.388 +
   1.389 +    // Clear working buffers
   1.390 +    pRateTransposer->clear();
   1.391 +    pTDStretch->clearInput();
   1.392 +    // yet leave the 'tempoChanger' output intouched as that's where the
   1.393 +    // flushed samples are!
   1.394 +}
   1.395 +
   1.396 +
   1.397 +// Changes a setting controlling the processing system behaviour. See the
   1.398 +// 'SETTING_...' defines for available setting ID's.
   1.399 +bool SoundTouch::setSetting(int settingId, int value)
   1.400 +{
   1.401 +    int sampleRate, sequenceMs, seekWindowMs, overlapMs;
   1.402 +
   1.403 +    // read current tdstretch routine parameters
   1.404 +    pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
   1.405 +
   1.406 +    switch (settingId) 
   1.407 +    {
   1.408 +        case SETTING_USE_AA_FILTER :
   1.409 +            // enables / disabless anti-alias filter
   1.410 +            pRateTransposer->enableAAFilter((value != 0) ? true : false);
   1.411 +            return true;
   1.412 +
   1.413 +        case SETTING_AA_FILTER_LENGTH :
   1.414 +            // sets anti-alias filter length
   1.415 +            pRateTransposer->getAAFilter()->setLength(value);
   1.416 +            return true;
   1.417 +
   1.418 +        case SETTING_USE_QUICKSEEK :
   1.419 +            // enables / disables tempo routine quick seeking algorithm
   1.420 +            pTDStretch->enableQuickSeek((value != 0) ? true : false);
   1.421 +            return true;
   1.422 +
   1.423 +        case SETTING_SEQUENCE_MS:
   1.424 +            // change time-stretch sequence duration parameter
   1.425 +            pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
   1.426 +            return true;
   1.427 +
   1.428 +        case SETTING_SEEKWINDOW_MS:
   1.429 +            // change time-stretch seek window length parameter
   1.430 +            pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
   1.431 +            return true;
   1.432 +
   1.433 +        case SETTING_OVERLAP_MS:
   1.434 +            // change time-stretch overlap length parameter
   1.435 +            pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
   1.436 +            return true;
   1.437 +
   1.438 +        default :
   1.439 +            return false;
   1.440 +    }
   1.441 +}
   1.442 +
   1.443 +
   1.444 +// Reads a setting controlling the processing system behaviour. See the
   1.445 +// 'SETTING_...' defines for available setting ID's.
   1.446 +//
   1.447 +// Returns the setting value.
   1.448 +int SoundTouch::getSetting(int settingId) const
   1.449 +{
   1.450 +    int temp;
   1.451 +
   1.452 +    switch (settingId) 
   1.453 +    {
   1.454 +        case SETTING_USE_AA_FILTER :
   1.455 +            return (uint)pRateTransposer->isAAFilterEnabled();
   1.456 +
   1.457 +        case SETTING_AA_FILTER_LENGTH :
   1.458 +            return pRateTransposer->getAAFilter()->getLength();
   1.459 +
   1.460 +        case SETTING_USE_QUICKSEEK :
   1.461 +            return (uint)   pTDStretch->isQuickSeekEnabled();
   1.462 +
   1.463 +        case SETTING_SEQUENCE_MS:
   1.464 +            pTDStretch->getParameters(NULL, &temp, NULL, NULL);
   1.465 +            return temp;
   1.466 +
   1.467 +        case SETTING_SEEKWINDOW_MS:
   1.468 +            pTDStretch->getParameters(NULL, NULL, &temp, NULL);
   1.469 +            return temp;
   1.470 +
   1.471 +        case SETTING_OVERLAP_MS:
   1.472 +            pTDStretch->getParameters(NULL, NULL, NULL, &temp);
   1.473 +            return temp;
   1.474 +
   1.475 +		case SETTING_NOMINAL_INPUT_SEQUENCE :
   1.476 +			return pTDStretch->getInputSampleReq();
   1.477 +
   1.478 +		case SETTING_NOMINAL_OUTPUT_SEQUENCE :
   1.479 +			return pTDStretch->getOutputBatchSize();
   1.480 +
   1.481 +		default :
   1.482 +            return 0;
   1.483 +    }
   1.484 +}
   1.485 +
   1.486 +
   1.487 +// Clears all the samples in the object's output and internal processing
   1.488 +// buffers.
   1.489 +void SoundTouch::clear()
   1.490 +{
   1.491 +    pRateTransposer->clear();
   1.492 +    pTDStretch->clear();
   1.493 +}
   1.494 +
   1.495 +
   1.496 +
   1.497 +/// Returns number of samples currently unprocessed.
   1.498 +uint SoundTouch::numUnprocessedSamples() const
   1.499 +{
   1.500 +    FIFOSamplePipe * psp;
   1.501 +    if (pTDStretch)
   1.502 +    {
   1.503 +        psp = pTDStretch->getInput();
   1.504 +        if (psp)
   1.505 +        {
   1.506 +            return psp->numSamples();
   1.507 +        }
   1.508 +    }
   1.509 +    return 0;
   1.510 +}

mercurial