【问题标题】:iPhone Audio Phase ShiftiPhone 音频相移
【发布时间】:2012-07-31 19:03:24
【问题描述】:

是否可以在 iPhone 上实时移相音频?

我有一个简单的应用程序设置,可以实时从麦克风输出音频。我想要做的是处理这个音频,即做一个相移。

我是否正确,唯一的方法是获取我的样本空间,进行 FFT,相移,然后逆 fft?

我知道 vDSP 库,但对于一项简单的任务来说,它的开销似乎太大了。

更新:我有来自 Elec Eng 的 DSP 基础,是的,我真的很想做相移。我不需要做频移或过滤,它们是单独的过程,我稍后会实施。

【问题讨论】:

  • 你是说频移?相移是无意义的和琐碎的。那只是原始版本的延迟版本。
  • 是的,我确实想移相。
  • 如果您只想执行相移,则根本不需要任何 DSP,因为这只是输入的时间延迟。只需读入音频数据并将其输出 - 这将为您引入相移。

标签: ios audio sdk signal-processing


【解决方案1】:

是的,这是一种方法。

这个示例 C 代码显示了它:

#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979324
#endif

typedef struct
{
  double x, y;
} tComplex;

tComplex complexAdd(const tComplex* a, const tComplex* b)
{
  tComplex c;
  c.x = a->x + b->x;
  c.y = a->y + b->y;
  return c;
}

tComplex complexMul(const tComplex* a, const tComplex* b)
{
  tComplex c;
  c.x = a->x * b->x - a->y * b->y;
  c.y = a->x * b->y + a->y * b->x;
  return c;
}

void dft(tComplex out[], const tComplex in[], size_t n, int direction)
{
  size_t k, i;
  for (k = 0; k < n; k++)
  {
    tComplex r = { 0, 0 }, e;
    for (i = 0; i < n; i++)
    {
      e.x = cos(-2 * direction * M_PI / n * ((double)k - n / 2) * ((double)i - n / 2));
      e.y = sin(-2 * direction * M_PI / n * ((double)k - n / 2) * ((double)i - n / 2));
      e = complexMul(&e, &in[i]);
      r = complexAdd(&r, &e);
    }
    out[k] = r;
  }
}

double maxAbs(const tComplex in[], size_t n)
{
  double m = 0;
  while (n--)
  {
    double a = hypot(in->x, in->y);
    if (m < a) m = a;
    in++;
  }
  return m;
}

#define SAMPLE_CNT   32
#define SAMPLE_SHIFT 3

int main(void)
{
  tComplex signalIn[SAMPLE_CNT];
  tComplex signalOut[SAMPLE_CNT];
  tComplex tmp[SAMPLE_CNT];
  int i;

  // signalIn[] = square pulse
  for (i = 0; i < SAMPLE_CNT; i++)
  {
    signalIn[i].x = ((i - SAMPLE_CNT / 2) >= 0) * ((i - SAMPLE_CNT / 2) < SAMPLE_CNT / 4);
    signalIn[i].y = 0;
  }

  // tmp[] = DFT{signalIn[]}
  dft(tmp, signalIn, SAMPLE_CNT, 1);

  // tmp[] = DFT{signalIn[]} * exp(j * 2 * PI * f * TimeShift)
  for (i = 0; i < SAMPLE_CNT; i++)
  {
    tComplex e;
    e.x =  cos(2 * M_PI * (i - SAMPLE_CNT / 2) * SAMPLE_SHIFT / SAMPLE_CNT);
    e.y = -sin(2 * M_PI * (i - SAMPLE_CNT / 2) * SAMPLE_SHIFT / SAMPLE_CNT);
    tmp[i] = complexMul(&tmp[i], &e);
  }

  // signalOut[] = IDFT{tmp[]}
  dft(signalOut, tmp, SAMPLE_CNT, -1);

  printf("     Re{In[]}    .     Im{In[]}    |"
         "   |DFT{In[]}|   |"
         "    Re{Out[]}    .    Im{Out[]}\n");

  for (i = 0; i < SAMPLE_CNT; i++)
  {
    int j, s;

    s = signalIn[i].x / maxAbs(signalIn, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);
    printf(".");
    s = signalIn[i].y / maxAbs(signalIn, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("|");

    s = hypot(tmp[i].x, tmp[i].y) / maxAbs(tmp, SAMPLE_CNT) * 8;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("|");

    s = signalOut[i].x / maxAbs(signalOut, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);
    printf(".");
    s = signalOut[i].y / maxAbs(signalOut, SAMPLE_CNT) * 8 + .5;
    for (j = -8; j <= 8; j++) printf("%c", " *"[s == j]);

    printf("\n");
  }

  return 0;
}

输出:

     Re{In[]}    .     Im{In[]}    |   |DFT{In[]}|   |    Re{Out[]}    .    Im{Out[]}
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |          *      |        *        .        *        
        *        .        *        |             *   |        *        .        *        
        *        .        *        |               * |        *        .        *        
                *.        *        |                *|        *        .        *        
                *.        *        |               * |        *        .        *        
                *.        *        |             *   |        *        .        *        
                *.        *        |          *      |                *.        *        
                *.        *        |        *        |                *.        *        
                *.        *        |         *       |                *.        *        
                *.        *        |         *       |                *.        *        
                *.        *        |         *       |                *.        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |         *       |                *.        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        

您可以看到,输出是输入的副本,在时间上移动了SAMPLE_SHIFT 个样本(此处为 3)。

您也可以将SAMPLE_SHIFT 更改为非整数值,从而获得小数样本的移位。

SAMPLE_SHIFT 的输出 = 2.5:

     Re{In[]}    .     Im{In[]}    |   |DFT{In[]}|   |    Re{Out[]}    .    Im{Out[]}
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |          *      |        *        .        *        
        *        .        *        |             *   |        *        .        *        
        *        .        *        |               * |        *        .        *        
                *.        *        |                *|        *        .        *        
                *.        *        |               * |        *        .        *        
                *.        *        |             *   |           *     .        *        
                *.        *        |          *      |                *.        *        
                *.        *        |        *        |              *  .        *        
                *.        *        |         *       |               * .        *        
                *.        *        |         *       |              *  .        *        
                *.        *        |         *       |               * .        *        
        *        .        *        |        *        |              *  .        *        
        *        .        *        |        *        |                *.        *        
        *        .        *        |         *       |           *     .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |        *        |        *        .        *        
        *        .        *        |         *       |        *        .        *        
        *        .        *        |        *        |        *        .        *        

【讨论】:

    【解决方案2】:

    您要做的应该对应于输出是输入与内核的折叠积分。它可以作为脉冲输入的输出找到。如果可以找到,请使用适当的数值积分方法进行折叠积分。这相当于一个 fft:s 只需要完成一次,其余的应该连续完成以连续提供输出。

    【讨论】:

    • 谢谢每个 Arve。我有 DSP 基础,是的,我确实想实时(或尽可能接近)进行相移。此外,我还将进行滤波。
    【解决方案3】:

    是否可以在 iPhone 上实时移相音频?

    嗯,是的,但这可能不是您真正想要做的。 (见 TJD 的评论)

    我有一个简单的应用程序设置,可以实时从麦克风输出音频。我想要做的是处理这个音频,即做一个相移。

    处理音频并不等同于进行相移。也许你想“过滤”?

    我是否正确,唯一的方法是获取我的样本空间,进行 FFT,相移,然后逆 fft?

    假设您要做的是过滤音频,那么不,我不推荐 FFT。您将遇到许多问题,例如窗口化、重叠/添加和性能。 FFT 不是设计滤波器的好方法。

    我知道 vDSP 库,但对于一项简单的任务来说,它的开销似乎太大了。

    我从未使用过 vDSP,但从快速检查来看,它看起来不像是正确的过滤解决方案。正确的解决方案更有可能使用简单的双二阶函数。你可以在这里阅读它们:

    http://en.wikipedia.org/wiki/Digital_biquad_filter

    这里有一本不错的“食谱”:

    http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt

    【讨论】:

    • 谢谢比约恩。我有 DSP 基础,是的,我确实想实时(或尽可能接近)进行相移。此外,我还将进行滤波。
    • 也许您有一个全通滤波器?我上面所说的一切都适用于此。
    【解决方案4】:

    应该可以在 iOS 设备上实时进行各种音频处理。有足够的处理能力。

    也许你可以更详细地描述你想要做什么样的相移。严格来说,“唯一”的方法是使用 FFT,但它可能是最好的方法,至少在性能和电池寿命方面是这样。

    当您说 vDSP 似乎有太多开销时,您指的是处理器性能还是工程工作量?我同意,vDSP 有一些不受欢迎的工程工作,主要是由于遗留接口需要将真实数据(例如输入信号)重新排列成奇偶分割。但是,其他方面,例如 FFT 数据的设置,是必要的;他们被 FFT 的性质所驱使。

    vDSP 例程的计算性能应该非常好。如果您在处理器性能或工程方面遇到困难,请report bugs(或功能请求)。

    【讨论】:

      【解决方案5】:

      我同意其他人的观点,即(实时前向)相移不需要 DSP,因为它只是音频样本的时间偏移。当然,实时反向相移比 DSP 需要更多的努力,因为它需要时间旅行。

      另一方面,我需要一种简单快速的方法来执行 iphone 麦克风音频样本的固定频移。实际上有几个频率偏移,例如 1 Hz 增量或大约。任何人都知道在 iPhone 上执行该任务的快速简便的方法吗?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        • 2011-04-10
        • 2017-09-28
        • 2010-12-18
        • 2010-11-03
        相关资源
        最近更新 更多