Roadmap "Dynamic Updates": any effort going on?

I've just tagged v0.10-alpha of the cores and v0.11-alpha of the Audio library.

Not much change to the cores, but they do align with Teensyduino 1.58. The Audio library has lots of fixes and features, in particular a copy of my buffered play/record objects - see https://forum.pjrc.com/threads/70963-Yet-Another-File-Player-(and-recorder).

I'm testing this but the resulting free memory is too small compared with the previous version I was using, not sure if it's because the last version of Teensyduino (because I wasusing an old version too) or the last version of the dynamic library, but the numbers are pretty different:
old version:
free for local variables:47008
free for malloc/new:194752
new version:
free for local variables:4160
free for malloc/new:6592

about the core, all I need to replace is the AudioStream.h & cpp like before?
 
I've done a very brief test (working on other stuff at the moment), and it looks like the newer compiler is producing bigger code. The memory usage difference between stock and dynamic Audio libraries is very similar, but the overall code size is about 1.5x with TD 1.58. Not great, but not a lot I can do about it... Setting "Smallest code" makes a big difference, but I've no idea what it does to execution speed. I don't understand why your heap space has shrunk so much, mine's the same for both versions of TeensyDuino:

Code:
Arduino 1.8.19, Teensyduino 1.58
cores + Audio stock
=======================================================================================
Memory Usage on Teensy 4.1:
  FLASH: code:128912, data:16660, headers:9048   free for files:7971844
   RAM1: variables:22144, code:123400, padding:7672   free for local variables:371072
   RAM2: variables:31488  free for malloc/new:492800

Arduino 1.8.19, Teensyduino 1.58
cores + Audio dynamic
=======================================================================================
Memory Usage on Teensy 4.1:
  FLASH: code:131104, data:16660, headers:8904   free for files:7969796
   RAM1: variables:22176, code:124808, padding:6264   free for local variables:371040
   RAM2: variables:31776  free for malloc/new:492512



Arduino 1.8.19, Teensyduino 1.57
cores + Audio stock
=======================================================================================
Memory Usage on Teensy 4.1:
  FLASH: code:83228, data:12560, headers:8656   free for files:8022020
   RAM1: variables:16064, code:77880, padding:20424   free for local variables:409920
   RAM2: variables:31488  free for malloc/new:492800


Arduino 1.8.19, Teensyduino 1.57
cores + Audio dynamic
=======================================================================================
Memory Usage on Teensy 4.1:
  FLASH: code:85772, data:12560, headers:9184   free for files:8018948
   RAM1: variables:16128, code:79480, padding:18824   free for local variables:409856
   RAM2: variables:31776  free for malloc/new:492512

Yes, just the AudioStream.cpp and .h need copying
 
sorry, I made a big mistake, because didn't adjust the buffer size in Audiostream,
also I'm using VSCode with PlatfomIO, no idea if it behaves differently from the Arduino IDE

this are the correct values, now heap is the same, but not the free space for local variables:

old Teensy version and old dynamic audio library:
teensy_size: FLASH: code:265360, data:35608, headers:8276 free for files:1722372
teensy_size: RAM1: variables:215136, code:261368, padding:776 free for local variables:47008
teensy_size: RAM2: variables:329536 free for malloc/new:194752

new Teensy version and new dynamic audio library:
teensy_size: FLASH: code:275812, data:42632, headers:8204 free for files:1704968
teensy_size: RAM1: variables:223296, code:270376, padding:24536 free for local variables:6080
teensy_size: RAM2: variables:329536 free for malloc/new:194752
 
I don't think VSCode / PlatformIO is the issue here, I think you've been unlucky...

Code space on Teensy 4.x is allocated in 32k chunks, with unused space shown as "padding". With the old TD's smaller code, your application just barely fits into 8 of those chunks (261368 + 776); but with the new one, it's 9 chunks (270376 + 24536). That loses 32k of your RAM1 space. Then the variables take an extra 223926-215136=8790 bytes, so you've lost 41558 bytes in all.

Suggestions you could try:
  • see if you can put some of your code in Flash using FLASHMEM, if it's infrequently used or doesn't need to run fast
  • optimise some code using #pragma GCC optimize ("Os") (please check this is right!)
  • figure out if you can to move some variables to RAM2 - they'll probably need extra code to allocate and initialise them, though
I'll take a look at some point and see if I've done something which results in extra RAM being used by the newer dynamic audio library, but even if I have it's only about 21% of the problem you're facing...

EDIT: don't think it can be the dynamic audio library, or it would show up as a huge difference to the stock library, let alone the previous version. The symbol maps don't seem to account for the ~6k difference I see, which is weird ... I must be missing something
 
Last edited:
hi again,
I have a question about the dynamic stereo mixer (which I think it's really good btw)
would like to totally deactivate the panning law, which number will set the gain correction depending on the pan setting to zero?
I've tried with "mixMaster.setPanLaw(0.0);" and it sounds distorted.
Thanks in advance!
 
Hi there. Not 100% sure what the question is - sorry! Here's a graph showing what the pan does for various pan law input values:
2023-06-22 21_15_38-Pan law.ods - LibreOffice Calc.png
I ought to put in a trap for setPanLaw(0.0) as it causes a division by zero. But a very small number gives close enough to linear; 0.707 should give constant audio power output from the left and right channels. If you want a left input to feed through to the left output at the set gain, and the right similarly, you could set the pan law to a very high value (say 1000). It won't quite make both channels have gain*1.0000, but it'll be very close.

I could put in a special case, say setPanLaw(-1.0) (or any number <0.0) to defeat the pan entirely and just apply the gain setting to each pair of channels. That would help efficiency if you use gain=1.0, as it skips the multiplication step when that's the case. Bear in mind the pan law applies to the entire mixer, you have to have several if you want different laws for each channel pair.
 
Hi h4yn0nnym0u5e, what I would like to get is a response like this:
Captura de pantalla 2023-08-22 a las 10.21.44.png
this would be the same response as setting the pan law to 0 in Cubase I think, which is better for mixng to my taste. It's true that phisically a sound will be louder when it's played by both speakers but 'psyco-acoustically' talking it will be perceibed more clearly when it's positioned only on one of the speakers as usually there are lots of elements in the center possition (like the bass, kick drum, lead insrument or voice)
...also I'm replacing the code in an existing project and I need the response to be as close as the earlier one so it sounds the same.

with setPanLaw(1000) there are some glitches & noises, so at the moment I'm using setPanLaw(2), which is close to what I would like but it will be awesome to have that option available.
 
Last edited:
Hi h4yn0nnym0u5e, what I would like to get is a response like this:
View attachment 31834
Hmmm ... OK, that'd be a special case, then. I'll give it some thought about how the API should work: on the face of it it's probably a separate function to set a flag which says "use the pan law gain value from ±1.0 to 0.0, and 1.0 gain on the 'other side'".
 
Thanks! btw I edited my previous post with some extra info while you were answering it... I'm slow wrinting in English haha
 
Not as slow as I'd be if I had to write in Spanish!

I found this discussion on a Reaper forum, not Cubase, but still seems relevant. It looks a bit of a weird response to my eyes on their various animated graphics, but it should be possible to do an approximation. I'll take a look.
 
OK, here's an entirely untested update to the AudioMixerStereo object. Keep a copy of the old versions, and drop these files into the Audio library: View attachment DynMixer.zip

To switch pan types, you need something like:
Code:
AudioMixerStereo myMixer{8}; // create 8-input stereo mixer
...
loop()
{
...
    myMixer.setPanType(AudioMixerStereo::PanLawType::analogue); // set the old panpot-like behaviour
// or
    myMixer.setPanType(AudioMixerStereo::PanLawType::DAW); // set the new DAW-like behaviour
...
}
I'll try to set up my own test fairly soon, but it'd be good to get your opinion on whether it's about right, or indeed completely broken...

I've implemented a quadratic curve, though the Reaper-related discussion was mentioning a sine (cosine, really) curve. It doesn't seem that different, and it's a much cheaper calculation:
2023-08-22 21_59_24-Pan law.ods - LibreOffice Calc.png
 
Thanks h4yn0nnym0u5e will check this new mixer in a moment.

I'm having a weird issue with the reverb effect, basically the reverb stops suddenly if there's no audio in the input, so when connected to a physical audio input it does work because there's always a subtle noise remaining but when used with digital audio sources like the sample player it does not. I've made a simple code to verify.

Code:
#include <Audio.h>
AudioInputTDM            tdmIn;  
AudioOutputTDM           tdmOut;     
AudioControlCS42448      cs42448_1; 
AudioSynthNoiseWhite      noise;
AudioEffectReverb        reverb;    
void setup()
{
    AudioMemory(200); 
    cs42448_1.enable();
    cs42448_1.volume(1.0);
    new AudioConnection(noise, 0, reverb, 0);
    new AudioConnection(reverb, 0,tdmOut, 0);
    reverb.reverbTime(3000);
}
void loop()
{
    noise.amplitude(1);
    delay(500);
    noise.amplitude(0);
    delay(3000);
}
 
Just tested the new dynamic mixer and it makes brutal noises when that setting is activated, if not it works fine.
 
Thanks h4yn0nnym0u5e will check this new mixer in a moment.

I'm having a weird issue with the reverb effect, basically the reverb stops suddenly if there's no audio in the input, so when connected to a physical audio input it does work because there's always a subtle noise remaining but when used with digital audio sources like the sample player it does not. I've made a simple code to verify.

Code:
#include <Audio.h>
AudioInputTDM            tdmIn;  
AudioOutputTDM           tdmOut;     
AudioControlCS42448      cs42448_1; 
AudioSynthNoiseWhite      noise;
AudioEffectReverb        reverb;    
void setup()
{
    AudioMemory(200); 
    cs42448_1.enable();
    cs42448_1.volume(1.0);
    new AudioConnection(noise, 0, reverb, 0);
    new AudioConnection(reverb, 0,tdmOut, 0);
    reverb.reverbTime(3000);
}
void loop()
{
    noise.amplitude(1);
    delay(500);
    noise.amplitude(0);
    delay(3000);
}
Inspecting the code, it's Yet Another Audio Object That Fails With Null Blocks. There are far too many of those in the Audio library ... I've fixed some and done PRs, which are being ignored, so I'm not doing any more. I'll try to fix it in the dynamic library at some point.

Just tested the new dynamic mixer and it makes brutal noises when that setting is activated, if not it works fine.
OK, I did say it was untested. When I get round to doing some fixing I'll take a look and try to figure out what I did wrong.
 
Hello Jonathan,

I’m working on a guitar pedal box and wish to be able to switch filters on the fly.
I searched the form and found the work you are doing to allow this.
I read for some hours on the subject, and I think I don’t understand the full picture.

1) I saw that from 1.57 supports “Dynamic AudioConnection objects”. If I understand the example, then this allows to change / disconnect / add “patchcords” on run time. is this correct?

2) I also read that you say that your code allows for “dynamic AudioStream objects”. I don’t understand what this means :( does this allow to add new filter on the run (like placing “AudioMixer4 mixer1;” in the main loop)?

3) In your github I found the full audio & core libraries. In some places I saw that you wrote that there is only a need to “replace AudioStream.cpp and AudioStream.h”. so why the full package in your github?


Thanks for your work!
 
Hi commi

Yes, Teensyduino 1.57 onward can freely create (with new), delete and connect patchcords, aka AudioConnection objects. So you can reconnect pre-existing filter objects in any order you like at run time. Not just filter objects, obviously, any object will work.

What you can’t do with the stock “static” library is create and destroy audio objects, which could be an issue if you occasionally want to use ones which consume a lot of CPU, like reverb or the ladder filter. As they’re created at boot time, they just sit there and use CPU, even if the connection path doesn’t use them*.

Enter the dynamic audio library, which does allow new and delete to be used to create and destroy audio objects. That would be the recommended way to do it, as placing AudioMixer4 mixer1; in loop() will repeatedly create it on entry and destroy it on exit, which is not likely to be useful!

The Audio library is for some reason in two parts: AudioStream.cpp and .h are in cores, and the rest in a more conventional library folder. I forked the whole cores package because that’s the only way to do it I knew at the time - there may be better options. I just clone the whole repo over the top of my Teensyduino install, and can then switch back to the standard cores if I need to. The library goes in my Arduino libraries folder as usual and thus gets used in preference, unless I move it to an unused-libraries folder, in which case Arduino doesn’t see it and I’m back to the standard set-up.

*Slightly off-topic note: sometimes a disconnected object doesn’t use (much) CPU, it depends on how it was written. It can also misbehave, often by stopping processing which should continue: from memory, the envelope, fade and one of the reverb objects are among the culprits. Where I know about them they’re fixed in the dynamic library. In some cases there’s a pull request for the static library, too, though recently I’ve not bothered because they never get pulled.
 
Hello Jonathan,

I’m working on a guitar pedal box and wish to be able to switch filters on the fly.
I searched the form and found the work you are doing to allow this.
I read for some hours on the subject, and I think I don’t understand the full picture.

1) I saw that from 1.57 supports “Dynamic AudioConnection objects”. If I understand the example, then this allows to change / disconnect / add “patchcords” on run time. is this correct?

2) I also read that you say that your code allows for “dynamic AudioStream objects”. I don’t understand what this means :( does this allow to add new filter on the run (like placing “AudioMixer4 mixer1;” in the main loop)?

3) In your github I found the full audio & core libraries. In some places I saw that you wrote that there is only a need to “replace AudioStream.cpp and AudioStream.h”. so why the full package in your github?


Thanks for your work!


I've recently encountered this very same situation; Teensy audio core unnecessarily using CPU cycles. My solution is to add enable()/disable() methods toggling a state flag to each object class. Said state flag is then checked per entry on to update() (where all the processing occurs), if not set, then function exits using zero CPU. Similar to the Delay object.
Result of this is a tuner (noteFreq) object that goes from using ~32% CPU time always to 32% only when in active use, else zero CPU usage.
I consider this a halfway house solution. It allows multiple objects to coexist but without the resource taxation.

IMO, all objects should have this functionality with the default state of disabled.
 
I've recently encountered this very same situation; Teensy audio core unnecessarily using CPU cycles. My solution is to add enable()/disable() methods toggling a state flag to each object class. Said state flag is then checked per entry on to update() (where all the processing occurs), if not set, then function exits using zero CPU. Similar to the Delay object.
Result of this is a tuner (noteFreq) object that goes from using ~32% CPU time always to 32% only when in active use, else zero CPU usage.
I consider this a halfway house solution. It allows multiple objects to coexist but without the resource taxation.

IMO, all objects should have this functionality with the default state of disabled.

I've just looked at the current cores, and I'm not sure all of your solution is needed. There's already an 'active' flag as part of the AudioStream base class, which if set to false will prevent the (derived) object from being updated. True, there's no way to set/clear it directly (though the isActive() function will tell you its state), but if you disconnect all of an object's inputs and outputs it should get cleared, and will be set again if a connection is subsequently made. Tell the truth, I'd forgotten about that feature of the disconnectable patchcords - thanks for the reminder! So I think your changes only need to be a setActive(bool) function added to the AudioStream class, which will then cover every derived class, of course.

The default state is actually active == false, but because the majority of designs have a connection to every object, no-one notices...
 
Thank you. I was unaware of AudioStream.cpp/h.
Dealing with CPU usage is half the problem, the other half being memory [block] usage. If we were to expand upon isActive()/setActive() & when set inactive, how would one then cleanly handle automatic memory block release?
 
One would have to tweak software_isr() in AudioStream.cpp to release() all the blocks transmitted to an inactive object. All the information needed is available to the base class, in the inputQueue and num_inputs members.
 
Back
Top