views:

70

answers:

2

The definition of AudioBufferList looks weird to me… i guess my C is not so good

struct AudioBufferList
{
    UInt32      mNumberBuffers;
    AudioBuffer mBuffers[kVariableLengthArray];
};
typedef struct AudioBufferList  AudioBufferList;

Why

AudioBuffer mBuffers[kVariableLengthArray];

and not

AudioBuffer *mBuffers;

?

kVariableLengthArray appears to be == 1. Eh?

I think i have it working but would appreciate it if anyone could set me straight.

A: 
AudioBuffer * mBuffers;

You are declaring a pointer to AudioBuffer
In such a case, you need to allocate enough memory, for instance with malloc().
Structures will be placed on the heap, and you will need to free the memory with free() after usage.

AudioBuffer mBuffers[ 1 ];

You are declaring an array of AudioBuffer. Memory is allocated automatically on the stack. No need to call free.

So when it's inside a structure, it's easier to work arrays, as you don't need to allocate memory explicitly to the structure member:

AudioBufferList myBufferList;

Otherwise, you would have to do:

AudioBufferList myBufferList;
myBufferList.mBuffers = malloc( sizeof( AudioBuffer ) * kVariableLengthArray );
/* ... */
free( myBufferList.mBuffers );
Macmade
Sorry, i might be being stupid but i still don't get it. You're saying that 'AudioBuffer mBuffers[ kVariableLengthArray ]' allocates memory for 1 AudioBuffer?
mustISignUp
How is that a list?
mustISignUp
It's an array, with only one element. But as the number of elements comes from a constant, maybe it will change in a future release. So declaring it in such a way leaves the possibility to have more AudioBuffers.
Macmade
+1  A: 

If the symbol kVariableLengthArray is a compile-time constant, this means that you can allocate a single buffer on the stack ("automatic" storage type, in C).

AudioBufferList single;

single.mNumberBuffers = 1;

Since the struct includes a count, it also means that you can create an instance with any number of buffers, like so:

AudioBufferList * get_n_buffers(size_t num)
{
    AudioBufferList *list;

    if(num < 1)
      return NULL;

    list = malloc(sizeof *list + (num - 1) * sizeof list->mBuffers[0]);
    list->mNumberBuffers = num;

    return list;
}

The latter code dynamically allocates an AudioBufferList, and relies on the fact that the array is last. So in memory, it will lookas if the AudioBuffer ends with the proper number of AudioBuffer instances, as the proper amount of space will be allocated.

unwind
ahhhh, i see. Great, thanks.
mustISignUp
Should that be 'malloc( sizeof(AudioBufferList) + (num-1)*sizeof( list->mBuffers[0]) );'? Thanks for your patience.
mustISignUp
@mustISignUp: no, I find it bad to repeat type names, and always prefer sizeof on expressions of the proper type. That way, if list were to change to a different pointer type, it would still do the rigtht thing. sizeof *list is exactly what I mean.
unwind
ok, thanks - point taken.
mustISignUp
I absolutely believe you but can't grasp how 'sizeof *list' is enough memory for a struct AudioBufferList…
mustISignUp
@mustISignUp: The variable "list" is of type "pointer to AudioBufferList", so the value of "sizeof *list" is the same as "sizeof (AudioBufferList)". If I had only said "sizeof list" it would be broken, it must be dereferenced to get the size of the object the pointer points at, i.e. AudioBufferList.
unwind
ok, i thought sizeof *list would be the size of the pointer, but of course it is the size of the object pointed to. Thanks for your help
mustISignUp
I prefer using offsetof: list = calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * num));
sbooth