Audio library depends on SdFat

jmarsh

Well-known member
This simple sketch results in the entire SdFat library being linked into the final binary:
Code:
#include <Audio.h>

void setup() {}
void loop() {}

These are the stats from compiling it:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:48028, data:6088, headers:8344   free for files:8064004
   RAM1: variables:8960, code:45224, padding:20312   free for local variables:449792
   RAM2: variables:12416  free for malloc/new:511872
Meanwhile without including Audio.h:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:8932, data:3016, headers:8528   free for files:8105988
   RAM1: variables:3808, code:6240, padding:26528   free for local variables:487712
   RAM2: variables:12416  free for malloc/new:511872

Is this expected? Why does Audio have a hard dependency on a filesystem?
 
Because the Audio library has the ability to play from audio whose source is from an SD card.
 
It should not pull in the SD library if those classes are never used. It's 40KB of wasted code space.
 
Poor separation of concerns if so - though the danger is needing lots of small glue libraries to fix such dependencies.
 
Yes, you're right, something in these libraries is causing unused code to be compiled into the final binary. I've added this to my low priority bug list.

Just to set expectations, this is a low priority if everything works properly and the only consequence is we use 40K of flash and 5K RAM1. Practically speaking, "low priority" means it maybe be a very long time until this gets any serious attention. So many other things are far more important.

However, on the medium priority list is reworking the file playing code to only depend on FS.h rather than SD.h & SdFat. Odds are strong that work will also end up solving this problem.
 
However, on the medium priority list is reworking the file playing code to only depend on FS.h rather than SD.h & SdFat. Odds are strong that work will also end up solving this problem.
That would likely solve this problem but in hindsight I think I've framed the issue poorly. The Audio library is really just a victim here.
The real issue lies either with the SD or SdFat libraries; as soon as they're included in the build Arduino compiles all the source code inside them and there's something (possibly related to the static SD class) that can't be eliminated as unreferenced by the compiler, which makes the linker include the lot. I'll do some more investigating; if something specific turns up I'll send a PR to the relevant git.

The only "real" issue I see with the Audio library is that a lot of the classes include static DMAChannels, which makes the compiler emit a bunch of code to destruct each of them (individually) upon program termination, i.e. registering functions with atexit. Would be nice if there's a gcc option to disable that since our programs never strictly terminate.
 
The only "real" issue I see with the Audio library is that a lot of the classes include static DMAChannels, which makes the compiler emit a bunch of code to destruct each of them (individually) upon program termination, i.e. registering functions with atexit. Would be nice if there's a gcc option to disable that since our programs never strictly terminate.
Turns out the fix for this problem is relatively simple: add "-fno-use-cxa-atexit" to the compiler options and it won't add code to destroy global objects at program termination. Saves a bit of space (code is still the same as post #1):
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:47024, data:6088, headers:8324   free for files:8065028
   RAM1: variables:8352, code:44320, padding:21216   free for local variables:450400
   RAM2: variables:12416  free for malloc/new:511872

The AudioOutputPWM class still contains a bit of baggage: it has two global static DMAChannels (i.e. not inside a static object) which get constructed at startup and presumably reserve a couple of channels for themselves.

Now back to trying to figure out why SD is not pruned by the linker...
 
This is quite the rabbit hole but here's a workaround:
Step 1: add "dot_a_linkage=true" to library.properties for the SD library.
Step 2: #include SD.h in the sketch before Audio.h
Result:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:10216, data:3016, headers:8268   free for files:8104964
   RAM1: variables:4512, code:7416, padding:25352   free for local variables:487008
   RAM2: variables:12416  free for malloc/new:511872

The linker can prune unused references from static libraries. So as long as the SD library gets turned into .a and gets put in the linker command line ahead of all the audio code objects, the bloaty unused global SD object goes away.
Probably if dot_a_linkage=true was also used for the audio library there would be even more code removed, but it's only valid for libraries using the Arduino 1.5+ library format.
 
Back
Top