问题
In iOS core audio there is the API AudioFileWritePackets that has a inPacketDescriptions
parameter defined as
'A pointer to an array of packet descriptions for the audio data.'
and it looks like this in the method signature:const AudioStreamPacketDescription *inPacketDescriptions,
now the struct AudioStreamPacketDescription is defined as follows:
struct AudioStreamPacketDescription
{
SInt64 mStartOffset;
UInt32 mVariableFramesInPacket;
UInt32 mDataByteSize;
};
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;
I would like to know how to create and populate such a 'pointer to an array of structs', or even once given the variable, how to read it. Using the speakHere example from apple I put a breakpoint where I receive the variable and tried to dump all its contents to the log.. this is an example of an attempt:
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");
}
}
any ideas?
Update: here is a sample log of me trying to mess around with it.. maybe it can help in the answer (notice at the bottom it keeps on appearing null.. it doesn't make sense that the whole thing is just a pack of zeroes since it's a variable returned by a callback that should be properly populated, also notice that it informs me about the number of packets I got back..).. also if i run the code with ((const AudioStreamPacketDescription *)(inPacketDescriptions +i))->mDataByteSize)
i get a EXC_BAD_ACCESS error
(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)
also here is what it looks like in the XCode inspector:

回答1:
I can't remember an instance where this specific struct is ever populated by you, the client. You will need to create storage for these structs which you then pass across multiple calls in order to successfully deal with reading and writing audio data. Several non-PCM formats will need this information, depending on how the audio data has been stored.
I would like to know how to create and populate such a 'pointer to an array of structs', or even once given the variable, how to read it.
Well, there are a handful of APIs in AudioFile I/O and AudioConvertor interfaces which use this structure. Basically, you don't populate this type yourself. The basic flow goes like this:
// 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.
(knowing the OP's problem in some more detail) In many cases, you can avoid all this complexity and simply create an ExtAudioFile
representation, and the specify kExtAudioFileProperty_ClientDataFormat
for your destination sample format -- then the ExtAudioFile APIs will create an internal convertor on your behalf which will convert the input of an audio file of an arbitrary type to some specified PCM representation which you can use for the playback sample data. Implementing all this at this level is actually quite complex if you want to support many file formats. ExtAudioFile makes converting the sample data very easy -- if that is an option and if it plays nicely with your streaming scenario.
As far as the logging, well you're attempting to print the fields of a NULL structure, from the looks of it.
回答2:
Declare an array of AudioStreamPacketDescription
:
AudioStreamPacketDescription descriptions[10];
/* Populate 'descriptions' */
descriptions[0].mVariableFramesInPacket = 4; /* For example. */
then pass it to the function:
/* 'a' is an instance of AQRecorder. */
a.printPacketDescriptionContents(descriptions, 10);
when an array is passed to a function it decays to a pointer (to the first element of the array).
The example printPacketDescriptionContents()
is only accessing the first element of the array inNumberPackets
times: you need to access each element:
NSLog(@"this is packetDescriptionArray[%d].mStartOffset: %lld",
i,
inPacketDescriptions[i].mStartOffset);
来源:https://stackoverflow.com/questions/12578524/how-to-read-assign-the-elements-of-a-pointer-that-points-to-an-array-of-structur