tsambou So how come logArray is interleaved?
actually, in hindsight, it can be considered as both interleaved and non-interleaved at the same time. And ultimately, it doesn't matter.
Explanation: an "interleaved" array has samples ordered first by frame, then by channel, so for instance, a 4-channel, 4-frames buffer looks like this:
"definition A"
float interleavedArray[numChannels * numFrames] = {A0, B0, C0, D0, A1, B1, C1, D1, A2, B2, C2, D2, A3, B3, C3, D3};
and you access a sample for a given frame
/channel
as:
float mySample = interleavedArray[frame * numChannels + channel];
where A
, B
, C
, D
are the channels and 0
, 1
, 2
, 3
are the frames.
A non-interleaved array has samples ordered first by channel, then by frame, so that same 4-channels/4-frames buffer looks like this when non-interleaved:
"definition B1"
float nonInterleavedArray1[numChannels * numFrames] = {A0, A1, A2, A3, B0, B1, B2, B3, C0, C1, C2, C3, D0, D1, D2, D3};
and you access a sample for a given frame
/channel
as:
float mySample = nonInterleavedArray1[channel * numFrames + frame];
The above definitions are pretty loose, so sometimes, as it is the case for the gen~
-generated code, the non-interleaved buffer is not, as in the above representation, made of a single array, but of an array of arrays:
"definition B2"
float arrayForChannelA[numFrames] = {A0, A1, A2, A3};
float arrayForChannelB[numFrames] = {B0, B1, B2, B3};
float arrayForChannelC[numFrames] = {C0, C1, C2, C3};
float arrayForChannelD[numFrames] = {D0, D1, D2, D3};
float* interleavedArray2[numChannels] = {arrayForChannelA, arrayForChannelB, arrayForChannelC, arrayForChannelD};
and you access a sample for a given frame
/channel
as:
float mySample = nonInterleavedArray2[channel][frame];
Now, what is logArray
? It is a buffer with nFakeChannels
channels and 1 frame. Given how there is only one frame, it looks the same if you think about it in terms of interleaved (definition A) or non-interleaved (definition B1):
A0 B0 C0 D0 ...
I believe that in an earlier iteration of the code, logArray
was the frame of samples that was being sent to the scope. Given how scope.log()
takes as an argument a pointer to an array of length nChannels
(where nChannels
is the first argument passed to scope.setup()
), there was need to create such an array in order to be able to call scope.log()
. All the elements of such array will be the value of the various channels you want to visualize on the scope, at a given frame
. That is exactly what logArray
was there for, and what happens in this loop:
tsambou
for(unsigned int i = 0; i < nFakeChannels; ++i)
{
logArray[i] = fakeChannels[i][n];
}
is that for a given frame n
, we are taking all the values from each channel i
(remember that fakeChannels
follows definition B2 above) and storing them in order into logArray
.
In your current version of the code you are then doing:
tsambou
for (unsigned int ch = 0; ch < context->audioOutChannels; ++ch){
audioWriteNI(context, n, ch,logArray[0]);
}
which takes the first element ([0]
) of logArray
and writes it to all the audio outputs. Given how you don't seem to be using the rest of the elements of logArray
, you could probably rewrite the whole section as:
for(unsigned int n = 0; n < context->audioFrames; ++n)
{
for (unsigned int ch = 0; ch < context->audioOutChannels; ++ch){
audioWriteNI(context, n, ch, fakeChannels[0][n]);
}
}
As to why you use audioWriteNI()
here and not audioWrite()
is because the Bela buffers themselves are - in this case - non-interleaved, and therefore you need to use the NI
version of the function. This is regardless of whatever else happens in your render()
code, d regardless of whether you are calling some gen~
-generated code, or whether you, or the library you are calling, are using interleaved or non-interleaved samples internally: as long as you request Bela to give you non-interleaved buffers (as we do in Bela_userSettings()
), then you are supposed to use theNI
version.
Hope this helps clarifying.