菜鸟教程小白 发表于 2022-12-13 07:29:58

ios - 从 AvassetReader 和 vDSP_FFT 获取 iPhone mp3 频率


                                            <p><p>我正在尝试从 iPhone/iPod 音乐库中为 iPod 库上的频谱应用程序获取频率,帮助自己解决 <a href="https://stackoverflow.com/questions/4972677/reading-audio-samples-via-avassetreader" rel="noreferrer noopener nofollow">reading-audio-samples-via-avassetreader</a>获取音频样本,然后使用 <a href="https://stackoverflow.com/questions/3398753/using-the-apple-fft-and-accelerate-framework" rel="noreferrer noopener nofollow">using-the-apple-fft-and-accelerate-framework</a>和 <a href="http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/vDSP_Programming_Guide/SampleCode/SampleCode.html#//apple_ref/doc/uid/TP40005147-CH205-45465" rel="noreferrer noopener nofollow">Apple vDSP Samples</a> ,但不知何故我在某个地方错了,无法计算频率。</p>

<p>所以一步一步来:</p>

<ul>
<li>阅读音频样本</li>
<li>汉宁窗</li>
<li>计算fft </li>
</ul>

<p>这是从 iPod mp3 库中获取频率的正确方法吗?</p>

<p>这是我的代码:</p>

<pre><code>static COMPLEX_SPLIT    A;
static FFTSetup         setupReal;
static uint32_t         log2n, n, nOver2;
static int32_t          stride;
static float            *obtainedReal;
static float            scale;

+ (void)initialize
{
    log2n = 10;
   n = 1 &lt;&lt; log2n;

    stride = 1;
    nOver2 = n / 2;
    A.realp = (float *) malloc(nOver2 * sizeof(float));
    A.imagp = (float *) malloc(nOver2 * sizeof(float));

    obtainedReal = (float *) malloc(n * sizeof(float));
    setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);
}


- (float) performAcceleratedFastFourierTransForAudioBuffer:(AudioBufferList)ioData   
{   
    NSUInteger * sampleIn = (NSUInteger *)ioData.mBuffers.mData;
    for (int i = 0; i &lt; nOver2; i++) {
    double multiplier = 0.5 * (1 - cos(2*M_PI*i/nOver2-1));
      A.realp = multiplier * sampleIn;
      A.imagp = 0;
    }

    memset(ioData.mBuffers.mData, 0, ioData.mBuffers.mDataByteSize);
    vDSP_fft_zrip(setupReal, &amp;A, stride, log2n, FFT_FORWARD);

    vDSP_zvmags(&amp;A, 1, A.realp, 1, nOver2);         

    scale = (float) 1.0 / (2 * n);

    vDSP_vsmul(A.realp, 1, &amp;scale, A.realp, 1, nOver2);
    vDSP_vsmul(A.imagp, 1, &amp;scale, A.imagp, 1, nOver2);

    vDSP_ztoc(&amp;A, 1, (COMPLEX *)obtainedReal, 2, nOver2);

    int peakIndex = 0;
    for (size_t i=1; i &lt; nOver2-1; ++i) {
      if ((obtainedReal &gt; obtainedReal) &amp;&amp; (obtainedReal &gt; obtainedReal))         
      {
            peakIndex = i;
            break;
      }
    }

    //here I don&#39;t know how to calculate frequency with my data   
    float frequency = obtainedReal / 44100 / n;

    vDSP_destroy_fftsetup(setupReal);
    free(obtainedReal);
    free(A.realp);
    free(A.imagp);

    return frequency;
}
</code></pre>

<p>我将 <code>1.485757</code> 和 <code>1.332233</code> 作为我的第一个频率</p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>在我看来,FFT 的复数输入转换存在问题。 <code>vDSP_ctoz()</code> 将实部和虚部交错的缓冲区拆分为两个缓冲区,一个实部,一个虚部。您对该函数的输入似乎只是已转换为 <code>COMPLEX</code> 的真实数据。这意味着 <code>vDSP_ctoz()</code> 的输入缓冲区只有所需长度的一半,并且超出缓冲区大小的一些垃圾数据正在转换。</p>

<p>您要么需要创建 <code>sampleOut</code> 的长度为 <code>2*n</code> 并设置所有其他值(实际部分),或者更好的是,您可以绕过 <code>vDSP_ctoz()</code> 并直接将您的输入数据复制到 <code>A.realp</code> 并将 <code>A.imagp</code> 设置为零。 <code>vDSP_ctoz()</code> 只有在与产生交错复杂数据的源接口(interface)时才需要。</p>

<p><strong>编辑</strong></p>

<p>好的,我认为我的第一个建议是错误的,因为 vDSP 文档说实到复就地 fft 的实际输入应格式化为拆分复数格式,以便 <code>imagp</code> 包含偶数样本,<code>realp</code> 包含奇数样本。我实际上并没有使用过 vDSP 库,但我熟悉很多其他 FFT 库,但我错过了这些细节。</p>

<p>在调用 <code>vDSP_zvmags(&A, 1, A.realp, 1, nOver2);</code> 之后,您应该能够使用 <code>A.realp</code> 找到峰值点,<code>A.realp</code> 应该包含 FFT 输出的幅度平方,它是标量。如果要进行缩放,应该在 mag2 操作之前完成,但如果您只是寻找峰值,则可能不需要。</p>

<p>要获得由 FFT 输出表示的实际频率,请使用以下公式:</p>

<pre><code>F = (i * Fs) / N,   i=0,1,...,N/2
</code></pre>

<p>在哪里</p>

<p><code>i</code> 是 FFT 输出缓冲区的索引
<code>Fs</code> 是音频采样率
<code>N</code> 是 FFT 长度</p>

<p>所以您的计算可能如下所示:</p>

<pre><code>float frequency = (peakIndex * 44100) / n;
</code></pre>

<p>请记住,vDSP 仅返回输入频谱的前半部分作为实际输入,因为后半部分是多余的。所以 FFT 输出表示从 <code>0</code> 到 <code>Fs/2</code> 的频率。</p>

<p>另一个注意事项是,我不知道您的寻峰算法是否会很好地工作,因为 FFT 输出不会平滑并且经常会有很多振荡。您只是在取两个相邻样本较低的第一个样本。如果您只想找到一个峰值,最好只找到整个输出的最大值。如果你想找到多个峰值,你将不得不做一些更复杂的事情。 </p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 从 AvassetReader 和 vDSP_FFT 获取 iPhone mp3 频率,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/5858494/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/5858494/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 从 AvassetReader 和 vDSP_FFT 获取 iPhone mp3 频率