1

在 iOS 核心音频中,API AudioFileWritePacketsinPacketDescriptions参数定义为“指向音频数据的数据包描述数组的指针”。

它在方法签名中看起来像这样:
const AudioStreamPacketDescription *inPacketDescriptions,

现在 struct AudioStreamPacketDescription 定义如下:

struct  AudioStreamPacketDescription
{
    SInt64  mStartOffset;
    UInt32  mVariableFramesInPacket;
    UInt32  mDataByteSize;
};
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;

我想知道如何创建和填充这样的“指向结构数组的指针”,甚至一旦给定变量,如何读取它。使用苹果的speakHere示例,我在接收变量的位置放置了一个断点,并尝试将其所有内容转储到日志中。这是一个尝试示例:

void AQRecorder::printPacketDescriptionContents(const AudioStreamPacketDescription * inPacketDescriptions, UInt32 inNumberPackets)
{
    for (int i = 0; i < inNumberPackets; ++i)
    {
        NSLog(@"\n----------------\n");
        NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld", i, (*inPacketDescriptions).mStartOffset);
        NSLog(@"this is packetDescriptionArray[%d].mVariableFramesInPacket: %lu", i, (*inPacketDescriptions).mVariableFramesInPacket);
        NSLog(@"this is packetDescriptionArray[%d].mDataByteSize: %lu", i, (*inPacketDescriptions).mDataByteSize);
        NSLog(@"\n----------------\n");   

    }        
}

有任何想法吗?

更新:这是我试图弄乱它的示例日志..也许它可以帮助回答(注意在底部它一直显示为空..整个事情只是一个包是没有意义的零,因为它是一个由回调返回的变量,应该正确填充,还请注意它告诉我我返回的数据包数量..).. 如果我运行代码,((const AudioStreamPacketDescription *)(inPacketDescriptions +i))->mDataByteSize)我得到一个 EXC_BAD_ACCESS 错误

(lldb) po **(inPacketDescriptions)
error: indirection requires pointer operand ('const AudioStreamPacketDescription' invalid)
error: 1 errors parsing expression
(lldb) po *(inPacketDescriptions)
(AudioStreamPacketDescription) $1 = [no Objective-C description available]
(lldb) po *(inPacketDescriptions).mStartOffset
error: member reference type 'const AudioStreamPacketDescription *' is a pointer; maybe you meant to use '->'?
error: indirection requires pointer operand ('SInt64' (aka 'long long') invalid)
error: 2 errors parsing expression
(lldb) po (*inPacketDescriptions).mStartOffset
(SInt64) $2 = 0 <nil>

(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)
(const class AudioStreamPacketDescription *) $3 = 0x00000010 [no Objective-C description available]
(lldb) po (const AudioStreamPacketDescription *)(inPacketDescriptions +1)->mStartOffset
error: Execution was interrupted, reason: Attempted to dereference an invalid pointer..
The process has been returned to the state before execution.
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mStartOffset
(SInt64) $5 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +1))->mDataByteSize
(UInt32) $6 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +100))->mDataByteSize
(UInt32) $7 = 0 <nil>
(lldb) po ((const AudioStreamPacketDescription *)(inPacketDescriptions +500))->mDataByteSize
(UInt32) $8 = 0 <nil>

(lldb) po inPacketDescriptions[0].mStartOffset
error: parent failed to evaluate: parent is NULL
(lldb) 

这也是 XCode 检查器中的样子:在此处输入图像描述

4

2 回答 2

1

声明一个数组AudioStreamPacketDescription

AudioStreamPacketDescription descriptions[10];

/* Populate 'descriptions' */
descriptions[0].mVariableFramesInPacket = 4; /* For example. */

然后将其传递给函数:

/* 'a' is an instance of AQRecorder. */
a.printPacketDescriptionContents(descriptions, 10);

当一个数组被传递给一个函数时,它会衰减到一个指针(指向数组的第一个元素)。

该示例printPacketDescriptionContents()仅访问数组inNumberPackets次的第一个元素:您需要访问每个元素:

NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld",
      i,
      inPacketDescriptions[i].mStartOffset);
于 2012-09-25T07:57:22.960 回答
1

我不记得您(客户)曾经填充过这个特定结构的实例。您将需要为这些结构创建存储,然后将其传递给多个调用,以便成功处理读取和写入音频数据。根据音频数据的存储方式,几种非 PCM 格式将需要此信息。

我想知道如何创建和填充这样的“指向结构数组的指针”,甚至一旦给定变量,如何读取它。

好吧,AudioFile I/O 和 AudioConvertor 接口中有一些 API 使用这种结构。基本上,您不会自己填充此类型。基本流程是这样的:

// this is not for PCM audio data
//
// we'll read up to 8 packets at a time:
const size_t MaxPacketsToRead(8);

// allocate MaxPacketsToRead ASPDs on the stack:
AudioStreamPacketDescription aspds[MaxPacketsToRead];

// audio file read function:
AudioFileID inAudioFile = ...;
Boolean inUseCache = ...;
UInt32 outNumBytes = ...;
AudioStreamPacketDescription* outPacketDescriptions(aspds);
SInt64 inStartingPacket = ...;
UInt32 ioNumPackets = MaxPacketsToRead; // << you may not get all the packets
                                        // you request, but this sets the
                                        // upper limit.
void* outBuffer = ...;


OSStatus result(AudioFileReadPackets(inAudioFile,
                                     inUseCache,
                                     &outNumBytes,
                                     outPacketDescriptions,
                                     inStartingPacket,
                                     &ioNumPackets,
                                     outBuffer
));

if (noErr != result) {
  ...uh-oh...
}

// *now* we know that we have ioNumPackets worth of valid ASPDs,
// populated by the reader. and we have the associated audio data
// in other parameters.
// we can now safely pass all this information off to a function which 
// reads the ASPDs, such as AudioFileWritePackets.

(更详细地了解 OP 的问题)在许多情况下,您可以避免所有这些复杂性并简单地创建一个ExtAudioFile表示,并指定kExtAudioFileProperty_ClientDataFormat您的目标样本格式 - 然后 ExtAudioFile API 将代表您创建一个内部转换器,它将将任意类型的音频文件的输入转换为可用于播放样本数据的某些指定 PCM 表示。如果您想支持多种文件格式,那么在这个级别实现所有这些实际上是相当复杂的。ExtAudioFile 使得转换样本数据变得非常容易——如果这是一个选项并且它与您的流媒体场景很好地配合。

至于日志记录,从外观上看,您正在尝试打印 NULL 结构的字段。

于 2012-09-25T09:29:50.900 回答