4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

サンプリングレート変換

Last updated at Posted at 2019-10-04

この資料をもとに行っていきます。
詳しいことは資料に載っているので一緒にご覧ください

・環境
Visual Studui 2019

# include <cmath>
# include <vector>
//変換パラメータ
struct ConvertParam
{
    //変換したいサンプリング周波数
    unsigned int sample;
    //内部アップサンプリング済みのサンプリング周波数
    unsigned int upSample;
    //カットオフ周波数
    unsigned int cutoff;
    //内部アップサンプリング倍率
    unsigned char rate;
    //変換後と変換前のずれ
    double gap;

    ConvertParam() {
        gap = sample = upSample = cutoff = rate = 0;
    }
};

//変換パラメータを求める
ConvertParam GetConvertParam(const unsigned int& befor, const unsigned int& affter)
{
    ConvertParam param;
    param.sample   = affter;
    param.rate     = char(std::fmax(befor, affter) / std::fmin(befor, affter)) + 1;
    param.upSample = befor * param.rate;
    param.cutoff   = int((std::fmin(befor, affter) / 2) * 0.9);
    param.gap      = (double(befor) / double(affter)) * param.rate;
    return param;
}

//理想次数を求める
unsigned short GetDegree(const unsigned char& siderope, const ConvertParam& param)
{
    double tmp1 = (double((param.sample / 2) * 0.9) / double(param.upSample / 2)) * std::acos(-1.0);
    double tmp2 = (double(param.sample / 2) / double(param.upSample / 2)) * std::acos(-1.0);
    double tmp  = tmp2 - tmp1;
    short r = short(std::ceil(double(siderope - 8) / (2.285 * tmp)));
    if (r % 2 != 0)
    {
        ++r;
    }
    return r;
}

//階乗
unsigned int Factorial(const unsigned int& n)
{
    unsigned int tmp = 1;
    for(unsigned int i = 1; i <= n; ++i)
    {
        tmp *= i;
    }
    return tmp;
}

//カイザー窓関数
double Kaizer(const unsigned char& siderope)
{
    if (21 < siderope && siderope < 50)
    {
        return 0.5842 * std::pow(double(siderope - 21), 0.4) + 0.07886 * double(siderope - 21);
    }
    else if (50 <= siderope)
    {
        return 0.1102 * (double(siderope) - 8.7);
    }
    return 0.0;
}

//第一種ベッセル関数
double Vessel(const double& val)
{
    double tmp = 1.0;
    for (unsigned int i = 1; i <= 20; ++i)
    {
        tmp += std::pow(std::pow(val / 2.0, i) / Factorial(i), 2.0);
    }
    return tmp;
}

//標本化関数
std::vector<double> Sinc(const unsigned char& siderope, const unsigned short& degree, const ConvertParam& param)
{
    std::vector<double>tmp(degree + 1);

    double kaizer = Kaizer(siderope);
    tmp[degree / 2] = (2.0 * double(param.cutoff)) / double(param.upSample);
    for (unsigned short i = 1; i <= degree / 2; ++i)
    {
        double win = Vessel(kaizer * std::sqrt(1.0 - std::pow(double(i) / double(degree / 2), 2.0))) / Vessel(kaizer);
        tmp[degree / 2 + i] = (std::sin(((2.0 * std::acos(-1.0) * double(param.cutoff)) / double(param.upSample)) * i) / (std::acos(-1.0) * i)) * win;
        tmp[degree / 2 - i] = tmp[degree / 2 + i];
    }
    return tmp;
}

//リサンプリング
std::vector<float> Resampling(const std::vector<double>& corre, const ConvertParam& param, const std::vector<float>& data, const unsigned int& sample, const unsigned char& channel)
{
    //変換後データ格納配列
    std::vector<float>convert(data.size() * (double(param.sample) / double(sample)), 0);
    unsigned int offset = (corre.size() - 1) / 2;
    unsigned int index = 0;
    double gap = param.gap;
    while (index < convert.size())
    {
        double integer = 0.0;
        gap = std::modf(gap, &integer);
        offset += int(integer);
        //変換
        for (size_t i = 0; i < (corre.size() - 1); ++i)
        {
            long tmp = offset - ((corre.size() - 1) / 2) + i;
            if (size_t(tmp / param.rate) * channel >= data.size())
            {
                break;
            }

            if (tmp % param.rate == 0)
            {
                //線形補完
                double comp = ((corre[i + 1] - corre[i]) * (1.0 - gap)) + corre[i];

                //二次補完
                if (i == ((corre.size() - 1) / 2) - 1)
                {
                    comp = ((corre[((corre.size() - 1) / 2) - 1] - corre[((corre.size() - 1) / 2)]) * std::pow(-gap, 2.0)) + corre[((corre.size() - 1) / 2)];
                }
                else if (i == (corre.size() - 1) / 2)
                {
                    comp = ((corre[((corre.size() - 1) / 2) - 1] - corre[((corre.size() - 1) / 2)]) * std::pow((1.0 - gap), 2.0)) + corre[((corre.size() - 1) / 2)];
                }

                for (unsigned char ch = 0; ch < channel; ++ch)
                {
                    if ((tmp / param.rate) * channel + ch < data.size())
                    {
                        convert[index + ch] += data[(tmp / param.rate) * channel + ch] * float(comp);
                    }
                }
            }
        }

        //ゲイン調節
        for (unsigned char ch = 0; ch < channel; ++ch)
        {
            convert[index + ch] *= param.rate - 0.2f;
        }

        index += channel;
        gap += param.gap;
    }
    return convert;
}

int main()
{
    std::vector<float>A(44100 * 2);
    for(size_t i = 0; i < A.size(); i += 2)
    {
        A[i] = 0.5f * std::sin(2.0f * std::acos(-1.0f) * 440.0f * i / 44100.0f);
        A[i + 1] = 0.5f * std::sin(2.0f * std::acos(-1.0f) * 440.0f * i / 44100.0f);
    }

    ConvertParam param = GetConvertParam(44100, 48000);
    unsigned short degree = GetDegree(100, param);
    std::vector<double>corre = Sinc(100, degree, param);
    std::vector<float>resample = Resampling(corre, param, A, 44100, 2);

    return 0;
}

資料に記載されている内容をそのままコードにしただけなので詳しい内容は資料を見ていただければわかると思います。
結果としては変換前と変換後に大きな誤差はなく問題なく音が聞こえてきます。
しかし、よく聞くとノイズが混じっていることが分かります。
この部分を改善できれば満足できる結果になると思われます。
多くの方の参考になれば嬉しいです。

4
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?