0

我目前正在学习在 iOS 中使用音频队列服务。为此,我遵循了音频队列服务编程指南 - 录制音频。这行得通。

现在我想在回调函数接收到来自麦克风的输入数据时更新一个 UI 元素(功率计)。这是我遇到问题的地方。

我不确定从回调函数调用视图/视图控制器以更新功率计的好方法是什么。我试过这个:在指南中描述的管理 state 的自定义结构中,我添加了一个指向我的视图控制器的指针:

struct AQRecorderState {
    AudioStreamBasicDescription  mDataFormat;                   // 2
    AudioQueueRef                mQueue;                        // 3
    AudioQueueBufferRef          mBuffers[kNumberBuffers];      // 4
    AudioFileID                  mAudioFile;                    // 5
    UInt32                       bufferByteSize;                // 6
    SInt64                       mCurrentPacket;                // 7
    bool                         mIsRunning;                    // 8
    void*                        mViewController; // <- I've added this line
};

当我开始录制时,我初始化了这个指针。这是在视图控制器的方法中完成的,当开始按钮接收到 TouchUp 事件时会调用该方法:

- (IBAction)startAudioRecording:(id)sender {

    struct AQRecorderState* aqData = createAQRecorderState();

    void* pViewControllerAsVoidPointer = (__bridge void*) self;
    aqData->mViewController = pViewControllerAsVoidPointer;

//...

分配结构的方法 createAQRecorderState() 如下所示:

struct AQRecorderState* createAQRecorderState() {
    struct AQRecorderState* pAqData = (struct AQRecorderState*) malloc(sizeof(AQRecorderState));

    initializeAQRecorderState(*pAqData); // sets members of the struct; does not set pointer to view controller

    return pAqData;
}

AFAIK,我在开始录制时创建的结构的实例被传递给以后对回调函数的调用。出于这个原因,我在回调函数中添加了以下代码,以便随后调用视图控制器:

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

//...

NSLog(@"interesting part start");

void* pViewControllerAsVoidPtr = pAqData->mViewController;
NSLog(@"localized pointer to void");

ViewController* pViewController = (__bridge ViewController*) pViewControllerAsVoidPtr;  //EXC_BAD_ACCESS
NSLog(@"localized pointer to ViewController");

//...
}

但是,当我进行桥接指针转换时,我在上面标记行的回调函数中得到了 EXC_BAD_ACCESS (code=1, address=0xffffffff)。然后我用调试器检查了这个,实际上pViewControllerAsVoidPtr 并没有保存我在pAqData->mViewController方法执行期间存储的地址startAudioRecording:

为什么那个指针会改变?或者:如何正确地从我的回调函数调用视图控制器来更新 UI?

4

1 回答 1

0

我发现了我的错误。这就是我如何执行创建录制音频队列的步骤。我完全按照指南中的复制粘贴操作:

AudioQueueNewInput (                              // 1
    &aqData.mDataFormat,                          // 2
    HandleInputBuffer,                            // 3
    &aqData, //<------------------ HERE           // 4
    NULL,                                         // 5
    kCFRunLoopCommonModes,                        // 6
    0,                                            // 7
    &aqData.mQueue                                // 8
);

问题是在指南中,变量 aqData 是一个结构,因此需要引用(前导 & 符号)将其转换为指针,而我的变量 aqData 已经是一个指向结构的指针,因此引用(前导 & sign) 将其更改为“指向结构的指针”。因此,我将指针的地址而不是结构的地址传递给了函数AudioQueueNewInput(...),这是错误的,当然,另一个位置的内存具有不同的内容。

编译器不会抱怨这种错误类型,因为它需要一个 (void*),它可以是任何指针,包括 (struct**),一个指向结构体指针的指针。

于 2012-11-29T12:01:49.400 回答