Resampling

リサンプリング

リサンプリングとは, デジタル信号をアナログ信号に戻し, サンプリング周波数を (本来のサンプリング周波数から) 変更して, 再度, サンプリングを実行するアルゴリズムです. リサンプリングを実行したあと, 本来のサンプリング周波数で再生すると, ピッチを変化させることができます.

線形補間

リサンプリングでは, サンプルとサンプルの中間値を求める処理が必要となります. そのための, 比較的, 簡単なアルゴリズムとして, 線形補間があります.

線形補間は, 以下の数式のように, サンプルとサンプルの中間値を比例配分によって算出します.

$ \begin{eqnarray} s(t)={\delta}(m+1)+(1-{\delta})s(m) \\
\end{eqnarray} $

ここで, ${\delta}$ は以下のように定義されます.

$ \begin{eqnarray} {\delta}=t-m\quad(m \leqq t < m+1) \\
\end{eqnarray} $

線形補間とサンプリング定理

もっとも, リサンプリングを正確に実行するには, サンプリング定理も考慮する必要があります.

サンプリング定理は, 以下の式によって, アナログ信号とデジタル信号を関連づけています.

$ \begin{eqnarray} s_{a}(t)=\sum_{n=-\infty}^{\infty}s_{d}(n)sinc({\pi}(t-n)) \end{eqnarray} $

線形補間は, それぞれのサンプルを頂点として, 三角形のハット関数を配置し, これらを加算することで, サンプルとサンプルの間を補間します.

一方で, サンプリング定理による補間は, それぞれのサンプルを頂点として, シンク関数を配置し, これらを加算することで, サンプルとサンプルの間を補間します (コンピュータで実装する場合, シンク関数を有限のサイズにうちきる必要があります).

線形補間は, 言うなれば, アナログ信号を折れ線で近似した処理にすぎないので, 厳密には, サンプリング定理による補間が, 本来のアナログ信号を, (可能な限り) 正確に求めるための唯一のアルゴリズムと言えます (ただし, アルゴリズムが複雑なほど, 計算量を要するので, アプリケーションによっては, 線形補間で十分なケースもあります).

実装

#include <vector>
#include <cmath>

enum {
  J = 24
};

double sinc(double x) {
  if (x == 0.0) {
    return 1.0;
  }

  return std::sin(x) / x;
}

void Resampling(
  double pitch,
  std::vector<double> &in,
  std::vector<double> &out,
  int length
) {
  if (pitch <= 0) {
    return;
  }

  for (int n = 0; n < (length / pitch); n++) {
    double t = pitch * n;
    int offset = static_cast<int>(t);

    for (int m = (offset - (J / 2)); m <= (offset + (J / 2)); m++) {
      if ((m >= 0) && (m < length)) {
        out[n] += in[m] * sinc(M_PI * (t - m));
      }
    }
  }
}

実行例

#include <iostream>
#include <cstdlib>
#include "wave.h"
#include "resampling.h"

int main(int argc, char **argv) {
  if (argc != 2) {
    std::cerr << "Require pitch" << std::endl;
    std::exit(EXIT_FAILURE);
  }

  STEREO_PCM pcm0, pcm1;

  double pitch = std::stod(argv[1]);

  if (pitch <= 0.0) {
    std::cerr << "pitch > 0.0" << std::endl;
    std::exit(EXIT_FAILURE);
  }

  wave_read(&pcm0, "stereo.wav");

  pcm1.fs     = pcm0.fs;
  pcm1.bits   = pcm0.bits;
  pcm1.length = static_cast<int>(pcm0.length / pitch);

  pcm1.sL.resize(pcm1.length);
  pcm1.sR.resize(pcm1.length);

  Resampling(pitch, pcm0.sL, pcm1.sL, pcm0.length);
  Resampling(pitch, pcm0.sR, pcm1.sR, pcm0.length);

  wave_write(&pcm1, "resampling.wav");
}

リファレンス

Share Comments
comments powered by Disqus