调用输入队列回调但没有数据

Posted

技术标签:

【中文标题】调用输入队列回调但没有数据【英文标题】:Input queue callback called but no data 【发布时间】:2017-01-15 11:48:46 【问题描述】:

为了开始接触 macOS 编程,我正在尝试制作一个简单的程序来录制来自输入设备(例如,我的 MacBook Pro 上的内置麦克风)的音频。我在 Xcode 中创建了一个 Objective-C Cocoa 项目,代码是 this tutorial from developer.apple.com. 的略微改编版本

这是我的代码:

// AppDelegate.m:  

#include <AudioToolbox/AudioToolbox.h>  

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification   

     struct AQRecorderState S;  

#define PRINT_R do\  
printf("%d: r = %d\n",__LINE__, r);\  
while(0)  

    AudiostreamBasicDescription *fmt = &S.mDataFormat;  

    fmt->mFormatID         = kAudioFormatLinearPCM;  
    fmt->mSampleRate       = 44100.0;  
    fmt->mChannelsPerFrame = 1;  
    fmt->mBitsPerChannel   = 32;  
    fmt->mBytesPerFrame    = fmt->mChannelsPerFrame * sizeof (float);  
    fmt->mFramesPerPacket  = 1;  
    fmt->mBytesPerPacket   = fmt->mBytesPerFrame * fmt->mFramesPerPacket;  
    fmt->mFormatFlags      = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved;  

    OSStatus r = 0;  

    r = AudioQueueNewInput(&S.mDataFormat, HandleInputBuffer, &S, NULL, kCFRunLoopCommonModes, 0, &S.mQueue);  

    PRINT_R;  

    UInt32 dataFormatSize = sizeof (S.mDataFormat);  

    r = AudioQueueGetProperty (  
                       S.mQueue,  
                       kAudioConverterCurrentInputStreamDescription,  
                       &S.mDataFormat,  
                       &dataFormatSize  
                       );  

    S.bufferByteSize = 22050;      

    for (int i = 0; i < NUM_BUFFERS; ++i)         
        r = AudioQueueAllocateBuffer(S.mQueue, S.bufferByteSize, &S.mBuffers[i]);  
        PRINT_R;  

        r = AudioQueueEnqueueBuffer(S.mQueue, S.mBuffers[i], 0, NULL);  
        PRINT_R;  
      

    S.mCurrentPacket = 0;        
    S.mIsRunning = true;        

    r = AudioQueueStart(S.mQueue, NULL);  
    PRINT_R;  

    r = AudioQueueStop(S.mQueue, true);  
    S.mIsRunning = false;  

    PRINT_R;  

    r = AudioQueueDispose(S.mQueue, true);  

这是我的输入回调函数(在单独的 C 文件中定义):

void HandleInputBuffer (  
                           void                                *aqData,             
                           AudioQueueRef                       inAQ,                
                           AudioQueueBufferRef                 inBuffer,            
                           const AudioTimeStamp                *inStartTime,        
                           UInt32                              inNumPackets,        
                           const AudioStreamPacketDescription  *inPacketDesc          
)   

    struct AQRecorderState *pAqData = (struct AQRecorderState *) aqData;               

    if (inNumPackets == 0 && pAqData->mDataFormat.mBytesPerPacket != 0)   
        inNumPackets =  
        inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;  
      
    printf("%f\n", *(float*)inBuffer->mAudioData);  
    if (pAqData->mIsRunning == 0)                                      
        return;  

    AudioQueueEnqueueBuffer(pAqData->mQueue, inBuffer, 0, NULL);  
  

当程序运行时,所有 Core Audio 函数调用都返回 0,这(我相信)代表“没有错误”,并且 HandleInputBuffer 被快速连续或几乎立即调用 NUM_BUFFERS 次(绝对不是每 0.5像 22050 的缓冲区大小这样的秒数会在这个采样率下建议),并且所有第一个样本都是 0.0。我在这里错过了什么?

【问题讨论】:

我的 C 生锈了。不用分配/mallocS吗?格式值在我看来也有些随意。为什么选择 32 位/通道?为什么是那些 formatFlags? 哦,对了,当调用 AudioQueueNewInput 并在 ASBD 中设置了无效值时,会给出一条错误消息(“fmt?”),其中包括默认输入设备的默认值,这就是我得到的地方值来自。另外,我相信AudioQueueAllocateBuffer 正在分配 AudioQueueNewInputfmt-&gt;mBitsPerChannel = 16fmt-&gt;mFormatFlags = kAudioFormatFlagIsSignedInteger 一起使用,但仍然没有输入。我有点想知道为什么内置麦克风会显示“io: 44100 Hz, 32-bit Float” 如果你链接到一个可运行的例子,我可以看看。 git clone http://github.com/elipp/inputqueue.git =) 【参考方案1】:

S.bufferByteSize 以字节为单位,而不是帧,所以 22050 字节不是半秒,而是22050/sizeof(float) 帧,所以大约是八分之一秒。

如果你想要半秒,试试

S.bufferByteSize = fmt->mSampleRate * fmt->mBytesPerFrame / 2; 

在您上面的代码中(以及您链接的 git 存储库中),您在 AudioQueueStart 之后立即 AudioQueueStopAudioQueueDispose 音频队列。不要那样做。

【讨论】:

非常正确,谢谢!但不幸的是不能解决我的问题:(回调仍然被立即调用NUM_BUFFERS次并且没有收到数据。 你看过 NSLog 时间戳吗?你怎么知道没有数据? 是的,我有。例如,当我上次运行它时,它们看起来像这样:2017-01-16 02:47:11.4394072017-01-16 02:47:11.4394402017-01-16 02:47:11.4394622017-01-16 02:47:11.4394732017-01-16 02:47:11.4394832017-01-16 02:47:11.439492NUM_BUFFERS = 6bufferByteSize = 44100 * 4 它们非常接近。你有我可以看的回购吗? 好的,所以我只是误解了注释(在原始苹果示例代码上)说“在用户界面线程上等待”;我假设AudioQueueStop 等待队列完成。问题解决了XD

以上是关于调用输入队列回调但没有数据的主要内容,如果未能解决你的问题,请参考以下文章

队列练习1

3185 队列练习 1 3186 队列练习 2

Codevs 队列练习 合并版

为啥我的音频队列输入回调中的数据包编号会有所不同?

数据结构-队列

SynchronousQueue 1.8 源码解析