tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SoundTouch.cpp (16353B)


      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 // License :
     45 //
     46 //  SoundTouch audio processing library
     47 //  Copyright (c) Olli Parviainen
     48 //
     49 //  This library is free software; you can redistribute it and/or
     50 //  modify it under the terms of the GNU Lesser General Public
     51 //  License as published by the Free Software Foundation; either
     52 //  version 2.1 of the License, or (at your option) any later version.
     53 //
     54 //  This library is distributed in the hope that it will be useful,
     55 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
     56 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     57 //  Lesser General Public License for more details.
     58 //
     59 //  You should have received a copy of the GNU Lesser General Public
     60 //  License along with this library; if not, write to the Free Software
     61 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     62 //
     63 ////////////////////////////////////////////////////////////////////////////////
     64 
     65 #include <assert.h>
     66 #include <stdlib.h>
     67 #include <memory.h>
     68 #include <math.h>
     69 #include <stdio.h>
     70 
     71 #include "SoundTouch.h"
     72 #include "TDStretch.h"
     73 #include "RateTransposer.h"
     74 #include "cpu_detect.h"
     75 
     76 using namespace soundtouch;
     77    
     78 /// test if two floating point numbers are equal
     79 #define TEST_FLOAT_EQUAL(a, b)  (fabs(a - b) < 1e-10)
     80 
     81 
     82 /// Print library version string for autoconf
     83 extern "C" void soundtouch_ac_test()
     84 {
     85    printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
     86 } 
     87 
     88 
     89 SoundTouch::SoundTouch()
     90 {
     91    // Initialize rate transposer and tempo changer instances
     92 
     93    pRateTransposer = new RateTransposer();
     94    pTDStretch = TDStretch::newInstance();
     95 
     96    setOutPipe(pTDStretch);
     97 
     98    rate = tempo = 0;
     99 
    100    virtualPitch = 
    101    virtualRate = 
    102    virtualTempo = 1.0;
    103 
    104    calcEffectiveRateAndTempo();
    105 
    106    samplesExpectedOut = 0;
    107    samplesOutput = 0;
    108 
    109    channels = 0;
    110    bSrateSet = false;
    111 }
    112 
    113 
    114 SoundTouch::~SoundTouch()
    115 {
    116    delete pRateTransposer;
    117    delete pTDStretch;
    118 }
    119 
    120 
    121 /// Get SoundTouch library version string
    122 const char *SoundTouch::getVersionString()
    123 {
    124    static const char *_version = SOUNDTOUCH_VERSION;
    125 
    126    return _version;
    127 }
    128 
    129 
    130 /// Get SoundTouch library version Id
    131 uint SoundTouch::getVersionId()
    132 {
    133    return SOUNDTOUCH_VERSION_ID;
    134 }
    135 
    136 
    137 // Sets the number of channels, 1 = mono, 2 = stereo
    138 void SoundTouch::setChannels(uint numChannels)
    139 {
    140    if (!verifyNumberOfChannels(numChannels)) return;
    141 
    142    channels = numChannels;
    143    pRateTransposer->setChannels((int)numChannels);
    144    pTDStretch->setChannels((int)numChannels);
    145 }
    146 
    147 
    148 // Sets new rate control value. Normal rate = 1.0, smaller values
    149 // represent slower rate, larger faster rates.
    150 void SoundTouch::setRate(double newRate)
    151 {
    152    virtualRate = newRate;
    153    calcEffectiveRateAndTempo();
    154 }
    155 
    156 
    157 // Sets new rate control value as a difference in percents compared
    158 // to the original rate (-50 .. +100 %)
    159 void SoundTouch::setRateChange(double newRate)
    160 {
    161    virtualRate = 1.0 + 0.01 * newRate;
    162    calcEffectiveRateAndTempo();
    163 }
    164 
    165 
    166 // Sets new tempo control value. Normal tempo = 1.0, smaller values
    167 // represent slower tempo, larger faster tempo.
    168 void SoundTouch::setTempo(double newTempo)
    169 {
    170    virtualTempo = newTempo;
    171    calcEffectiveRateAndTempo();
    172 }
    173 
    174 
    175 // Sets new tempo control value as a difference in percents compared
    176 // to the original tempo (-50 .. +100 %)
    177 void SoundTouch::setTempoChange(double newTempo)
    178 {
    179    virtualTempo = 1.0 + 0.01 * newTempo;
    180    calcEffectiveRateAndTempo();
    181 }
    182 
    183 
    184 // Sets new pitch control value. Original pitch = 1.0, smaller values
    185 // represent lower pitches, larger values higher pitch.
    186 void SoundTouch::setPitch(double newPitch)
    187 {
    188    virtualPitch = newPitch;
    189    calcEffectiveRateAndTempo();
    190 }
    191 
    192 
    193 // Sets pitch change in octaves compared to the original pitch
    194 // (-1.00 .. +1.00)
    195 void SoundTouch::setPitchOctaves(double newPitch)
    196 {
    197    virtualPitch = exp(0.69314718056 * newPitch);
    198    calcEffectiveRateAndTempo();
    199 }
    200 
    201 
    202 // Sets pitch change in semi-tones compared to the original pitch
    203 // (-12 .. +12)
    204 void SoundTouch::setPitchSemiTones(int newPitch)
    205 {
    206    setPitchOctaves((double)newPitch / 12.0);
    207 }
    208 
    209 
    210 void SoundTouch::setPitchSemiTones(double newPitch)
    211 {
    212    setPitchOctaves(newPitch / 12.0);
    213 }
    214 
    215 
    216 // Calculates 'effective' rate and tempo values from the
    217 // nominal control values.
    218 void SoundTouch::calcEffectiveRateAndTempo()
    219 {
    220    double oldTempo = tempo;
    221    double oldRate = rate;
    222 
    223    tempo = virtualTempo / virtualPitch;
    224    rate = virtualPitch * virtualRate;
    225 
    226    if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
    227    if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
    228 
    229 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
    230    if (rate <= 1.0f) 
    231    {
    232        if (output != pTDStretch) 
    233        {
    234            FIFOSamplePipe *tempoOut;
    235 
    236            assert(output == pRateTransposer);
    237            // move samples in the current output buffer to the output of pTDStretch
    238            tempoOut = pTDStretch->getOutput();
    239            tempoOut->moveSamples(*output);
    240            // move samples in pitch transposer's store buffer to tempo changer's input
    241            // deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
    242 
    243            output = pTDStretch;
    244        }
    245    }
    246    else
    247 #endif
    248    {
    249        if (output != pRateTransposer) 
    250        {
    251            FIFOSamplePipe *transOut;
    252 
    253            assert(output == pTDStretch);
    254            // move samples in the current output buffer to the output of pRateTransposer
    255            transOut = pRateTransposer->getOutput();
    256            transOut->moveSamples(*output);
    257            // move samples in tempo changer's input to pitch transposer's input
    258            pRateTransposer->moveSamples(*pTDStretch->getInput());
    259 
    260            output = pRateTransposer;
    261        }
    262    } 
    263 }
    264 
    265 
    266 // Sets sample rate.
    267 void SoundTouch::setSampleRate(uint srate)
    268 {
    269    // set sample rate, leave other tempo changer parameters as they are.
    270    pTDStretch->setParameters((int)srate);
    271    bSrateSet = true;
    272 }
    273 
    274 
    275 // Adds 'numSamples' pcs of samples from the 'samples' memory position into
    276 // the input of the object.
    277 void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
    278 {
    279    if (bSrateSet == false) 
    280    {
    281        ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
    282    } 
    283    else if (channels == 0) 
    284    {
    285        ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
    286    }
    287 
    288    // accumulate how many samples are expected out from processing, given the current 
    289    // processing setting
    290    samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
    291 
    292 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
    293    if (rate <= 1.0f) 
    294    {
    295        // transpose the rate down, output the transposed sound to tempo changer buffer
    296        assert(output == pTDStretch);
    297        pRateTransposer->putSamples(samples, nSamples);
    298        pTDStretch->moveSamples(*pRateTransposer);
    299    } 
    300    else 
    301 #endif
    302    {
    303        // evaluate the tempo changer, then transpose the rate up, 
    304        assert(output == pRateTransposer);
    305        pTDStretch->putSamples(samples, nSamples);
    306        pRateTransposer->moveSamples(*pTDStretch);
    307    }
    308 }
    309 
    310 
    311 // Flushes the last samples from the processing pipeline to the output.
    312 // Clears also the internal processing buffers.
    313 //
    314 // Note: This function is meant for extracting the last samples of a sound
    315 // stream. This function may introduce additional blank samples in the end
    316 // of the sound stream, and thus it's not recommended to call this function
    317 // in the middle of a sound stream.
    318 void SoundTouch::flush()
    319 {
    320    int i;
    321    int numStillExpected;
    322    SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
    323 
    324    // how many samples are still expected to output
    325    numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
    326    if (numStillExpected < 0) numStillExpected = 0;
    327 
    328    memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
    329    // "Push" the last active samples out from the processing pipeline by
    330    // feeding blank samples into the processing pipeline until new, 
    331    // processed samples appear in the output (not however, more than 
    332    // 24ksamples in any case)
    333    for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
    334    {
    335        putSamples(buff, 128);
    336    }
    337 
    338    adjustAmountOfSamples(numStillExpected);
    339 
    340    delete[] buff;
    341 
    342    // Clear input buffers
    343    pTDStretch->clearInput();
    344    // yet leave the output intouched as that's where the
    345    // flushed samples are!
    346 }
    347 
    348 
    349 // Changes a setting controlling the processing system behaviour. See the
    350 // 'SETTING_...' defines for available setting ID's.
    351 bool SoundTouch::setSetting(int settingId, int value)
    352 {
    353    int sampleRate, sequenceMs, seekWindowMs, overlapMs;
    354 
    355    // read current tdstretch routine parameters
    356    pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
    357 
    358    switch (settingId) 
    359    {
    360        case SETTING_USE_AA_FILTER :
    361            // enables / disabless anti-alias filter
    362            pRateTransposer->enableAAFilter((value != 0) ? true : false);
    363            return true;
    364 
    365        case SETTING_AA_FILTER_LENGTH :
    366            // sets anti-alias filter length
    367            pRateTransposer->getAAFilter()->setLength(value);
    368            return true;
    369 
    370        case SETTING_USE_QUICKSEEK :
    371            // enables / disables tempo routine quick seeking algorithm
    372            pTDStretch->enableQuickSeek((value != 0) ? true : false);
    373            return true;
    374 
    375        case SETTING_SEQUENCE_MS:
    376            // change time-stretch sequence duration parameter
    377            pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
    378            return true;
    379 
    380        case SETTING_SEEKWINDOW_MS:
    381            // change time-stretch seek window length parameter
    382            pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
    383            return true;
    384 
    385        case SETTING_OVERLAP_MS:
    386            // change time-stretch overlap length parameter
    387            pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
    388            return true;
    389 
    390        default :
    391            return false;
    392    }
    393 }
    394 
    395 
    396 // Reads a setting controlling the processing system behaviour. See the
    397 // 'SETTING_...' defines for available setting ID's.
    398 //
    399 // Returns the setting value.
    400 int SoundTouch::getSetting(int settingId) const
    401 {
    402    int temp;
    403 
    404    switch (settingId) 
    405    {
    406        case SETTING_USE_AA_FILTER :
    407            return (uint)pRateTransposer->isAAFilterEnabled();
    408 
    409        case SETTING_AA_FILTER_LENGTH :
    410            return pRateTransposer->getAAFilter()->getLength();
    411 
    412        case SETTING_USE_QUICKSEEK :
    413            return (uint)pTDStretch->isQuickSeekEnabled();
    414 
    415        case SETTING_SEQUENCE_MS:
    416            pTDStretch->getParameters(NULL, &temp, NULL, NULL);
    417            return temp;
    418 
    419        case SETTING_SEEKWINDOW_MS:
    420            pTDStretch->getParameters(NULL, NULL, &temp, NULL);
    421            return temp;
    422 
    423        case SETTING_OVERLAP_MS:
    424            pTDStretch->getParameters(NULL, NULL, NULL, &temp);
    425            return temp;
    426 
    427        case SETTING_NOMINAL_INPUT_SEQUENCE :
    428        {
    429            int size = pTDStretch->getInputSampleReq();
    430 
    431 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
    432            if (rate <= 1.0)
    433            {
    434                // transposing done before timestretch, which impacts latency
    435                return (int)(size * rate + 0.5);
    436            }
    437 #endif
    438            return size;
    439        }
    440 
    441        case SETTING_NOMINAL_OUTPUT_SEQUENCE :
    442        {
    443            int size = pTDStretch->getOutputBatchSize();
    444 
    445            if (rate > 1.0)
    446            {
    447                // transposing done after timestretch, which impacts latency
    448                return (int)(size / rate + 0.5);
    449            }
    450            return size;
    451        }
    452 
    453        case SETTING_INITIAL_LATENCY:
    454        {
    455            double latency = pTDStretch->getLatency();
    456            int latency_tr = pRateTransposer->getLatency();
    457 
    458 #ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
    459            if (rate <= 1.0)
    460            {
    461                // transposing done before timestretch, which impacts latency
    462                latency = (latency + latency_tr) * rate;
    463            }
    464            else
    465 #endif
    466            {
    467                latency += (double)latency_tr / rate;
    468            }
    469 
    470            return (int)(latency + 0.5);
    471        }
    472 
    473        default :
    474            return 0;
    475    }
    476 }
    477 
    478 
    479 // Clears all the samples in the object's output and internal processing
    480 // buffers.
    481 void SoundTouch::clear()
    482 {
    483    samplesExpectedOut = 0;
    484    samplesOutput = 0;
    485    pRateTransposer->clear();
    486    pTDStretch->clear();
    487 }
    488 
    489 
    490 /// Returns number of samples currently unprocessed.
    491 uint SoundTouch::numUnprocessedSamples() const
    492 {
    493    FIFOSamplePipe * psp;
    494    if (pTDStretch)
    495    {
    496        psp = pTDStretch->getInput();
    497        if (psp)
    498        {
    499            return psp->numSamples();
    500        }
    501    }
    502    return 0;
    503 }
    504 
    505 
    506 /// Output samples from beginning of the sample buffer. Copies requested samples to 
    507 /// output buffer and removes them from the sample buffer. If there are less than 
    508 /// 'numsample' samples in the buffer, returns all that available.
    509 ///
    510 /// \return Number of samples returned.
    511 uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
    512 {
    513    uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
    514    samplesOutput += (long)ret;
    515    return ret;
    516 }
    517 
    518 
    519 /// Adjusts book-keeping so that given number of samples are removed from beginning of the 
    520 /// sample buffer without copying them anywhere. 
    521 ///
    522 /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
    523 /// with 'ptrBegin' function.
    524 uint SoundTouch::receiveSamples(uint maxSamples)
    525 {
    526    uint ret = FIFOProcessor::receiveSamples(maxSamples);
    527    samplesOutput += (long)ret;
    528    return ret;
    529 }
    530 
    531 
    532 /// Get ratio between input and output audio durations, useful for calculating
    533 /// processed output duration: if you'll process a stream of N samples, then 
    534 /// you can expect to get out N * getInputOutputSampleRatio() samples.
    535 double SoundTouch::getInputOutputSampleRatio()
    536 {
    537    return 1.0 / (tempo * rate);
    538 }