计算 iOS 音频功率峰值

发布于 2024-11-13 06:41:35 字数 1756 浏览 5 评论 0原文

由于渐进的见解编辑了问题:-)

我正在创建一个正在监听音频输入的应用程序。 我希望它能够计算峰值。 (峰值的最大频率约为 10 Hz。)

经过大量搜索,我最终使用了 AudioQueue 服务,因为它将能够为我提供原始输入数据。 我使用的是 SpeakHere 示例的精简版本(无播放),但我不想简单地将缓冲区写入文件系统,而是想查看各个示例数据。

我认为我现在走在正确的轨道上,但我不明白如何使用缓冲区。 我正在尝试分离一个样本的数据。那么下面的函数中的 for 循环是否有意义,并且 我应该在里面放什么才能得到一份样品?

void AQRecorder::MyInputBufferHandler( void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription* inPacketDesc)
{
    // AudioQueue callback function, called when an input buffers has been filled.

    AQRecorder *aqr = (AQRecorder *)inUserData;
    try {
        if (inNumPackets > 0) {
            /*          // write packets to file
            XThrowIfError(AudioFileWritePackets(aqr->mRecordFile,FALSE,inBuffer->mAudioDataByteSize,inPacketDesc,aqr->mRecordPacket,&inNumPackets,inBuffer->mAudioData),
                      "AudioFileWritePackets failed");*/

            SInt16 sample;
        for (UInt32 sampleIndex=0; sampleIndex < inNumPackets; ++sampleIndex) {


            // What do I put here to look at one sample at index sampleIndex ??


        }
        aqr->mRecordPacket += inNumPackets;
        }

    // if we're not stopping, re-enqueue the buffe so that it gets filled again
    if (aqr->IsRunning())
        XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL),
                      "AudioQueueEnqueueBuffer failed");
    } catch (CAXException e) {
    char buf[256];
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
}

(也许我不应该删除这么多原来的问题......政策是什么?)

最初我正在考虑使用 AurioTouch 示例,但正如评论中指出的那样,它使用吞吐量,我只需要输入。这也是一个比 SpeakHere 复杂得多的示例。

Edited the question due to progressive insights :-)

I am creating an app that is listening to the audio input.
I want it to count peaks. (peaks will be at a max frequency of about 10 Hz.)

After a lot of searching, I ended up using the AudioQueue Service as that will be able to give me the raw input data.
I am using a stripped down version (no playback) of the SpeakHere example, but instead of simply writing the buffer to the filesystem, I want to look at the individual sample data.

Think I am on the right track now, but I don't understand how to work with the buffers.
I am trying to isolate the data of one sample. So that for loop in the following function, does that make any sense, and
what should I put in there to get one sample?

void AQRecorder::MyInputBufferHandler( void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription* inPacketDesc)
{
    // AudioQueue callback function, called when an input buffers has been filled.

    AQRecorder *aqr = (AQRecorder *)inUserData;
    try {
        if (inNumPackets > 0) {
            /*          // write packets to file
            XThrowIfError(AudioFileWritePackets(aqr->mRecordFile,FALSE,inBuffer->mAudioDataByteSize,inPacketDesc,aqr->mRecordPacket,&inNumPackets,inBuffer->mAudioData),
                      "AudioFileWritePackets failed");*/

            SInt16 sample;
        for (UInt32 sampleIndex=0; sampleIndex < inNumPackets; ++sampleIndex) {


            // What do I put here to look at one sample at index sampleIndex ??


        }
        aqr->mRecordPacket += inNumPackets;
        }

    // if we're not stopping, re-enqueue the buffe so that it gets filled again
    if (aqr->IsRunning())
        XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL),
                      "AudioQueueEnqueueBuffer failed");
    } catch (CAXException e) {
    char buf[256];
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
}

(maybe I shouldn't have deleted so much of the original question... what is the policy?)

Originally I was thinking of using the AurioTouch example, but as was pointed out in a comment, that uses throughput and I only need input. It is also a much more complicated example than SpeakHere.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花间憩 2024-11-20 06:41:35

你可能想对你的峰值功率水平应用某种平滑,也许是 IIR 滤波器,比如:

x_out = 0.9 * x_old + 0.1 * x_in;
:
x_old = x_out;

我没有使用过这个功能,所以我不知道它是否会做你想要的一切。如果没有,您可以降低一个级别并使用 RemoteIO 音频单元,并使用“输入回调”捕获声音(而不是当扬声器渴望数据时发生的渲染回调)

请注意在输入回调中,您必须创建自己的缓冲区,不要认为仅仅因为您得到一个缓冲区指针作为最后一个参数,就意味着它指向有效的东西。事实并非如此。

无论如何,您可以使用一些 vDSP 函数来获取整个缓冲区向量的幅度平方(1024 个浮点或任何缓冲区大小/流格式)

,然后您可以自己平滑它

you would probably want to apply some sort of smoothing to your peak power level, maybe am IIR filter, something like:

x_out = 0.9 * x_old + 0.1 * x_in;
:
x_old = x_out;

I haven't used this feature, so I don't know if it would do everything you want. if it doesn't, you can drop a level and use a RemoteIO audio unit, and catch sound as it comes in using the 'input callback' ( as opposed to the render callback which happens when the speakers are hungry for data )

note that in the input callback you have to create your own buffers, don't think just because you get a buffer pointer as the last parameter that that means it points to something valid. it doesn't.

anyway, you could use some vDSP function to get the magnitude squared for the vector of the entire buffer (1024 floats or whatever your buffer size / stream format is)

and then you could smooth that yourself

深爱不及久伴 2024-11-20 06:41:35

这将循环遍历缓冲区中的所有样本。

    SInt16 sample;
    for (UInt32 sampleIndex=0; sampleIndex < inNumPackets; ++sampleIndex) {
        sample = buffer[sampleIndex]; // Get the power of one sample from the buffer
        aqr->AnalyseSample(sample);
    }

这是一个棘手的部分:aqr 指向记录器的实例。回调是静态函数,不能直接访问成员变量或成员函数。

为了计算峰值,我记录了长期平均值和短期平均值。如果短期平均值比长期平均值大某个因子,则存在峰值。当短期均线再次下跌时,高峰已经过去。

This loops through all samples in the buffer.

    SInt16 sample;
    for (UInt32 sampleIndex=0; sampleIndex < inNumPackets; ++sampleIndex) {
        sample = buffer[sampleIndex]; // Get the power of one sample from the buffer
        aqr->AnalyseSample(sample);
    }

Was a tricky part: aqr points to the instance of the recorder. The callback is a static function and can't access the member variables or member functions directly.

In order to count the peaks, I keep track of a longterm average and a shortterm average. If the shortTerm average is a certain factor bigger than the longterm average, there is a peak. When the shortterm average goes down again, the peak has passed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文