Roadmap "Dynamic Updates": any effort going on?

Hi @h4yn0nnym0u5e , I'm trying to use the AudioEffectDelayExternal from your dynamic library but I can't make it work. The psram chip I'm using is a APS6404L-3SQR, I've tried these 2 options when declaring the delay object (it enters in a reboot cycle with the first and get stuck with the second):
Code:
AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_PSRAM64, 1000);
AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_EXTMEM, 1000);

I'm also using the psram for some arrays with EXTMEM but there's plenty of free memory. The rest of the code is not relevant as at the moment I'm just declaring the delay object.

Do you have any suggestion?

Thanks
 
it worked with this
Code:
AudioEffectDelayExternal delayExt1(AUDIO_MEMORY_EXTMEM, 100);
...but with a pretty small delay size, if I put bigger numbers it does get stuck : /
 
Hi @M4ngu . That's odd, it should work. Not a lot of time to investigate right now, but here's some code that works for me. You want the second line if you have EXTMEM, the first is for PSRAM on the audio adaptor.
C++:
#include <Audio.h>

AudioSynthWaveformSine sine;
AudioEffectEnvelope env;
AudioEffectDelayExternal dly1(AUDIO_MEMORY_EXTMEM,  200.0f, false);
AudioEffectDelayExternal dly2(AUDIO_MEMORY_EXTMEM, 1200.0f, false);
AudioMixer4 mix;
AudioAnalyzePeak peak;
AudioOutputI2S headphones;

AudioConnection patchCord1(sine, env);
/*
AudioConnection patchCord2(env, dly1);
AudioConnection patchCord3(env, dly2);
/*/
AudioConnection patchCord1a(env, 0, mix, 2);
AudioConnection patchCord2(mix, dly1);
AudioConnection patchCord3(mix, dly2);
//*/
AudioConnection patchCord4(dly1, 0, mix, 0);
AudioConnection patchCord5(dly2, 0, mix, 1);
AudioConnection patchCord5b(dly2, 1, mix, 1);
AudioConnection patchCord6(dly1, 0, headphones, 0);
AudioConnection patchCord7(mix, 0, headphones, 1);
AudioConnection patchCord8(mix, 0, peak, 0);

AudioControlSGTL5000 audioShield;

void setup() {
  AudioMemory(10);

  audioShield.enable();
  audioShield.volume(0.27);

  sine.amplitude(0.75);
  sine.frequency(220.0f);

  dly1.delay(0,  183.0f);
  dly2.delay(0, 1091.0f);
  dly2.delay(1,  777.0f);

  mix.gain(0,0.6f);
  mix.gain(1,0.35f);
  mix.gain(3,0.4f);

  env.release(30.0f);

  size_t s = 100000;
  void* p = extmem_malloc(s);
  Serial.printf("Could allocate %d at %08X\n",s,(uint32_t) p);
  extmem_free(p);
}

void loop()
{
  if (peak.available())
  {
    if (peak.read() < 0.8f)
    {
      env.noteOn();
      delay(10);
      env.noteOff();
      Serial.print("loop ");
    }
    else
      Serial.print("wait ..... ");
    
    delay(2500);
  }
  Serial.println();
}
 
In the version I have the delay constructor expects the memory type and delay size but not that bool at the end, could you point me to the files for that?
Code:
AudioEffectDelayExternal dly2(AUDIO_MEMORY_EXTMEM, 1200.0f, false);
I've also removed all use of EXTMEM in my code to be sure that it wasn't the issue and still the same, it works only with a really small delay size.
 
Last edited:
The latest version is in the relevant repo branch. I've been a bit lax about tagging changes, although I tend to do so only when a new Teensyduino is released, in an attempt to ensure that you can grab the latest, make the changes to cores, import the dynamic audio library and it'll all work OK. Keeping up with betas etc. usually isn't worth it.

The extra bool at the end was only briefly useful, during a period when the dreaded Static Initialisation Fiasco was causing delay objects to try to run before the SPI hardware had been started. Paul and other clever people worked out a fix for that, so you should be able to omit it safely, assuming your Teensyduino is at 1.59 or better. I think. I don't really recall the timeline...
 
The latest version is in the relevant repo branch. I've been a bit lax about tagging changes, although I tend to do so only when a new Teensyduino is released, in an attempt to ensure that you can grab the latest, make the changes to cores, import the dynamic audio library and it'll all work OK. Keeping up with betas etc. usually isn't worth it.

The extra bool at the end was only briefly useful, during a period when the dreaded Static Initialisation Fiasco was causing delay objects to try to run before the SPI hardware had been started. Paul and other clever people worked out a fix for that, so you should be able to omit it safely, assuming your Teensyduino is at 1.59 or better. I think. I don't really recall the timeline...
thanks a lot! it works now, I was using the master branch...
 
Last edited:
Yes, that should be right. At some point after Teensyduino 1.60 is released I’ll try to make sure those get updated to match; it may be a while, do report back if me being lazy causes any problems :)
 
Hi again @h4yn0nnym0u5e

I have a pretty simple question about the safest way to work with dynamic audio objects and static connections.
In the process of deleting audio objects and creating new ones, should I do that after AudioNoInterrupts() as you do in your example PlaySynthDynamic.ino?
also, should the object be disconnected before being deleted?
if so, should the disconnection happen also after AudioNoInterrupts()? asking because I see there's already a __disable_irq(); inside the disconnect() function.

Here is a basic sketch which has not much sense itself but illustrates what I'm asking for:
Code:
#include <Arduino.h>

AudioMixer4 mix;
AudioConnection cord;
AudioStream *voice = NULL;
// here some code for the codec or DAC

void setup()
{
  AudioMemory(20);
  // here some code to connect the mixer to codec/DAC
}

void loop()
{
  delay(500);
  AudioNoInterrupts();
  cord.disconnect();
  if(NULL != voice)
  {
    delete voice;
    voice == NULL;
  }
  voice = new AudioSynthWaveform;
  cord.connect(voice, 0, mix, 0);
  AudioInterrupts();
 
  // here some code to init/make voice sound
}
 
Looking at this comment, it appears I decided that the programmer can't always wrap delete calls with Audio[No]Interrupts(), so no, you don't need to do so. Because the object is marked as inactive very early in the destruction process, there is no risk of an update() call executing a partially destroyed object. I think.

Any incoming blocks should be safely released as part of the derived object's destruction, because it's gone by the time the base AudioStream is destroyed; and all the connections are automatically disconnected in ~AudioStream(), so there's no need for you to disconnect() in your code.

There is also no need to check for voice being NULL (or more correctly, nullptr), because the C++ standard guarantees this is safe. And there's an actuall error in your code: voice == NULL is wrong!

So your loop() code boils down to:
C++:
void loop()
{
  delay(500);
    
  delete voice;
  voice = nullptr; // good practice, though obviously not needed here...
    
  voice = new AudioSynthWaveform; // ...because of this!
  cord.connect(voice, 0, mix, 0);
 
  // here some code to init/make voice sound
}
 
ups...sorry for the ==,
I was using that code because I saw it in the PlaySynthDynamic.ino example, there you have it like this:
Code:
if (NULL != waves[chan])
  {
    delete waves[chan];
    waves[chan]=NULL;   
// ...and so on...

thanks for taking some time to answer my questions and for the great library ;-)
 
I probably wrote that code before I knew about the C++ guarantee … and I’ve seen plenty of places in the Teensy libraries where the check is made, too. And other places where it would be useful if a method did a similar check, but doesn’t, like AudioStream::release().

No worries - thanks for using it!
 
Back
Top