ReceiveWritable, ReceiveReadOnly returning NULL blocks

grinch

Well-known member
Hi, I am experimenting with writing a surround panner object for the Teensy Audio Library. This reads a single input channel and applies panning gain to 8 output channels. I notice I'm getting some weird behavior with the receive blocks functions. Paired things down to the bare minimum and I'm finding the problem is with blocks not being received properly:

Code:
void Panner::update(void){
  for(int och = 0; och < num_channels; ++och){
    audio_block_t *block = receiveReadOnly(0);
    if (block) {
      transmit(block, och);
      release(block);
    }else{
      Serial.print("Lost block "); Serial.println(och);
    }
  }

This code shows the blocks being lost for channels 1-7, and received for 0. I get the same behavior for receiveWritable(och).

Can these functions only be called once? I need 8 copies of the input signal to apply gain to, thought the receive functions would do this.

Seeing as this doesn't work, what is the best practice way to copy the input signal to 8 separate buffers so I can apply gain to all of them?

Thanks!
 
You can only receive a block once. The right thing to do here is to ReceiveWritable() one source block, allocate() 7 more, apply gain to the source block as you copy it to your 7 new blocks, transmitting and releasing those, then apply gain to the source block and transmit and release it. “Obviously” deal with cases where you can’t allocate all the blocks you want…
 
My thoughts from looking at the mixer object:

Code:
void Panner::update(void){
  for(int och = 0; och < num_channels; ++och){
    audio_block_t *block = receiveReadOnly(0);          [COLOR="#FF0000"]You need receiveReadOnly( och )    BUT[/COLOR]
    if (block) {
      transmit(block, och);     [COLOR="#FF0000"]You can't successfully transmit a receive only block[/COLOR]
      release(block);
    }else{
      Serial.print("Lost block "); Serial.println(och);
    }
  }






Code:
void AudioMixer4::update(void)
{
	audio_block_t *in, *out=NULL;
	unsigned int channel;

	for (channel=0; channel < 4; channel++) {
		if (!out) {
			out = receiveWritable(channel);
			if (out) {
				int32_t mult = multiplier[channel];
				if (mult != MULTI_UNITYGAIN) applyGain(out->data, mult);
			}
		} else {
			in = receiveReadOnly(channel);
			if (in) {
				applyGainThenAdd(out->data, in->data, multiplier[channel]);
				release(in);
			}
		}
	}
	if (out) {
		transmit(out);
		release(out);
	}
}
 
Any active audio block in the system has a single source, but may have multiple destinations. If you use receiveReadOnly() then you promise not to modify the block's content (you may not be the only destination...). If you use receiveWritable() the audio system allocates a new block, copies the source audio into it, and returns that. This is your own private copy, so you can of course modify and transmit it. There's nothing to stop you transmitting a receiveReadOnly() block, but it's probably rarely useful. Internally, if there's actually only one destination (remaining) for an audio block, and receiveWritable() is used, then the allocate / copy operations are omitted. You don't need to know or care about this...

The first time a module calls either of the receive() functions, the block is returned and the input is marked as "used", which is why only the first call succeeds.

The AudioMixer4 uses receiveWritable() to get a modifiable block for onward transmission, but then only needs receiveReadOnly(), because it only transmits a single block, and doing that saves the allocate / copy space and time.
 
Back
Top