Добавьте сглаживание / полосу ограничения для зацикленного wav-образца (НЕ преобразование Фурье) - PullRequest
0 голосов
/ 24 марта 2019

Как построить интерполяцию сглаживания с использованием кода C ++? У меня есть простой 4096 или 1024 буфера. Конечно, когда я играю это на высоких частотах, у меня возникают проблемы с алиасами. чтобы избежать этого, сигнал должен быть ограничен полосой пропускания на высоких частотах. Грубо говоря, «пилообразная» волна на высоких частотах должна выглядеть как обычный синус. И это то, что я хочу получить, чтобы мой звук не звучал грязно, как если бы вы двигали ручки в своем старом FM / AM-радио в своей машине.

Я знаю, как построить ограниченный по площади квадрат, треугольник, пилообразную с помощью преобразования Фурье. Так что мой вопрос только о wavetable

1 Ответ

0 голосов
/ 28 мая 2019

Нашли решение в источниках AudioKit.Один буфер будет разделен на 10 буферов / октав.Поэтому, когда вы воспроизводите звук, вы не воспроизводите исходную волну, а воспроизводите семпл, подготовленный для определенной октавы.

Импорт в ваш проект WaveStack.hpp

namespace AudioKitCore
{

    // WaveStack represents a series of progressively lower-resolution sampled versions of a
    // waveform. Client code supplies the initial waveform, at a resolution of 1024 samples,
    // equivalent to 43.6 Hz at 44.1K samples/sec (about 23.44 cents below F1, midi note 29),
    // and then calls initStack() to create the filtered higher-octave versions.
    // This provides a basis for anti-aliased oscillators; see class WaveStackOscillator.

    struct WaveStack
    {
        // Highest-resolution rep uses 2^maxBits samples
        static constexpr int maxBits = 10;  // 1024

        // maxBits also defines the number of octave levels; highest level has just 2 samples
        float *pData[maxBits];

        WaveStack();
        ~WaveStack();

        // Fill pWaveData with 1024 samples, then call this
        void initStack(float *pWaveData, int maxHarmonic=512);

        void init();
        void deinit();

        float interp(int octave, float phase);
    };

}

WaveStack.cpp

#include "WaveStack.hpp"
#include "kiss_fftr.h"

namespace AudioKitCore
{

    WaveStack::WaveStack()
    {
        int length = 1 << maxBits;                  // length of level-0 data
        pData[0] = new float[2 * length];           // 2x is enough for all levels
        for (int i=1; i<maxBits; i++)
        {
            pData[i] = pData[i - 1] + length;
            length >>= 1;
        }
    }

    WaveStack::~WaveStack()
    {
        delete[] pData[0];
    }

    void WaveStack::initStack(float *pWaveData, int maxHarmonic)
    {
        // setup
        int fftLength = 1 << maxBits;
        float *buf = new float[fftLength];
        kiss_fftr_cfg fwd = kiss_fftr_alloc(fftLength, 0, 0, 0);
        kiss_fftr_cfg inv = kiss_fftr_alloc(fftLength, 1, 0, 0);

        // copy supplied wave data for octave 0
        for (int i=0; i < fftLength; i++) pData[0][i] = pWaveData[i];

        // perform initial forward FFT to get spectrum
        kiss_fft_cpx spectrum[fftLength / 2 + 1];
        kiss_fftr(fwd, pData[0], spectrum);

        float scaleFactor = 1.0f / (fftLength / 2);

        for (int octave = (maxHarmonic==512) ? 1 : 0; octave < maxBits; octave++)
        {
            // zero all harmonic coefficients above new Nyquist limit
            int maxHarm = 1 << (maxBits - octave - 1);
            if (maxHarm > maxHarmonic) maxHarm = maxHarmonic;
            for (int h=maxHarm; h <= fftLength/2; h++)
            {
                spectrum[h].r = 0.0f;
                spectrum[h].i = 0.0f;
            }

            // perform inverse FFT to get filtered waveform
            kiss_fftri(inv, spectrum, buf);

            // resample filtered waveform
            int skip = 1 << octave;
            float *pOut = pData[octave];
            for (int i=0; i < fftLength; i += skip) *pOut++ = scaleFactor * buf[i];
        }

        // teardown
        kiss_fftr_free(inv);
        kiss_fftr_free(fwd);
        delete[] buf;
    }

    void WaveStack::init()
    {
    }

    void WaveStack::deinit()
    {
    }

    float WaveStack::interp(int octave, float phase)
    {
        while (phase < 0) phase += 1.0;
        while (phase >= 1.0) phase -= 1.0f;

        int nTableSize = 1 << (maxBits - octave);
        float readIndex = phase * nTableSize;
        int ri = int(readIndex);
        float f = readIndex - ri;
        int rj = ri + 1; if (rj >= nTableSize) rj -= nTableSize;

        float *pWaveTable = pData[octave];
        float si = pWaveTable[ri];
        float sj = pWaveTable[rj];
        return (float)((1.0 - f) * si + f * sj);
    }

}

Затем используйте его следующим образом:

//wave and outputWave should be float[1024];
void getSample(int octave, float* wave, float* outputWave){

    uint_fast32_t impulseCount = 1024;

    if (octave == 0){
        impulseCount = 737;
    }else if (octave == 1){
        impulseCount = 369;
    }
    else if (octave == 2){
        impulseCount = 185;
    }
    else if (octave == 3){
        impulseCount = 93;
    }
    else if (octave == 4){
        impulseCount = 47;
    }
    else if (octave == 5){
        impulseCount = 24;
    }
    else if (octave == 6){
        impulseCount = 12;
    }
    else if (octave == 7){
        impulseCount = 6;
    }
    else if (octave == 8){
        impulseCount = 3;
    }
    else if (octave == 9){
        impulseCount = 2;
    }

    //Get sample for octave
    stack->initStack(wave, impulseCount);

    for (int i = 0; i < 1024;i++){
        float phase = (1.0/float(1024))*i;

        //get interpolated wave and apply volume compensation
        outputWave[i] = stack->interp(0, phase) / 2.0;
    }

}

Тогда... когда 10 буферов готово.Вы можете использовать их при воспроизведении звука.Используя этот код, вы можете получить индекс буфера / октавы в зависимости от вашей частоты

uint_fast8_t getBufferIndex(const float& frequency){

    if (frequency >= 0 && frequency < 40){
        return 0;
    }
    else if (frequency >= 40 && frequency < 80){
        return 1;
    }else if (frequency >= 80 && frequency < 160){
        return 2;
    }else if (frequency >= 160 && frequency < 320){
        return 3;
    }else if (frequency >= 320 && frequency < 640){
        return 4;
    }else if (frequency >= 640 && frequency < 1280){
        return 5;
    }else if (frequency >= 1280 && frequency < 2560){
        return 6;
    }else if (frequency >= 2560 && frequency < 5120){
        return 7;
    }else if (frequency >= 5120 && frequency < 10240){
        return 8;
    }else if (frequency >= 10240){
        return 9;
    }
    return 0;
}

Так что, если я знаю, что моя частота ноты 440 Гц.Тогда для этой заметки я получаю волну следующим образом:

float notInterpolatedSound[1024];
float interpolatedSound[1024];

uint_fast8_t octaveIndex = getBufferIndex(440.0);
getSample(octaveIndex, notInterpolatedSound, interpolatedSound);

//tada! 

пс.код выше является фильтром нижних частот.Я также попытался сделать sinc-интерполяцию.Но Синк работал для меня очень дорого и не совсем так.Хотя, возможно, я сделал это неправильно.

...