Bandpass Filter to SD card

Hello there,

I’m working w/ some other folks here (jcooper, shaneL) on the ASA project. I’m really struggling writing to the SD card after passing audio through a band pass filter I modeled based on the example.

The filter passes audio through to the audio output, but writing to SD card causes errors. Raw audio to SD card works fine.

Here are the patch cords (yes the input is single channel):

AudioConnection_F32       patchCord1(i2s_in, 0, audioEffectBandpassFD, 0);   //connect the Left input to our algorithm
AudioConnection_F32       patchCord2(audioEffectBandpassFD, 0, i2s_out, 0);  //connect the algorithm to the left output
AudioConnection_F32       patchCord3(audioEffectBandpassFD, 0, i2s_out, 1);  //connect the algorithm to the right output
AudioConnection_F32       patchCord4(audioEffectBandpassFD, 0, audioSDWriter, 0); //connect audio to left channel of SD writer
AudioConnection_F32       patchCord5(audioEffectBandpassFD, 0, audioSDWriter, 1); //connect audio to right channel of SD writer

Here’s the error it spits out:

AudioControlAIC3206: Received Error During goToPage(): Error = 4
AudioControlAIC3206: Received Error During writeRegister(): Error = 4
AudioControlAIC3206: Received Error During writeRegister(): Error = 4
AudioControlAIC3206: Received Error During writeRegister(): Error = 4
AudioControlAIC3206: Received Error During writeRegister(): Error = 4
AudioControlAIC3206: Received Error During goToPage(): Error = 4

When I try to activate SD recording it says stuff like this over and over (presumably, each time it services the SD card in a loop):

AudioSDWriter: chan 0, data skip? This ID = 0, Previous ID = 13570355
AudioSDWriter: chan 1, data skip? This ID = 0, Previous ID = 13570355

Any idea what I might be doing wrong?

Thanks.

Oo! Sorry about the trouble! The error messages that you posted makes it look like your problems have nothing to do with filtering or with the SD card. It looks more like a basic problem with the main processor not really being able to talk with the audio chip. Weird. Bummer. I wonder why?

Is there any way that you can share your whole program? Like posting it here? Or sharing a GitHub link?

I wanna help!

Chip

Hi Chip,

I got rid of all my custom mods and only used the lowpass filter in the examples and the provided SD card writer, in case it was my error (still might be!). I get the same error messages.

How’s a pastebin?
Lowpass + SDwrite - Pastebin.com

Note that I commented out the PRINT_OVERRUN_WARNING section for debugging purposes… doesn’t seem to matter either way.

Sorry for the delayed reply…I was on the road. I’m on this now!

I’ve grabbed your code. It references another file “AudioEffectLowpass_FD_F32.h”, which I’m assuming is the same as from the Tympan Library’s example “LowpassFilter_FD”. Is that true? Or did you modify it in some way?

Chip

I’ve recreated your error messages! That means that I can maybe fix it!

OK, I think that I’ve found your problem. In your code, in setup(), you correctly have the line:

myTympan.enable(); // activate AIC

Unfortunately, I think that it comes a bit later than it should. When I move the line earlier, like before your myTympan.inputSelect() lines, the error messages went away:

 //Enable the Tympan to start the audio flowing!
  myTympan.enable(); // activate AIC

  //Choose the desired audio input on the Typman...this will be overridden by the serviceMicDetect() in loop()
  //myTympan.inputSelect(TYMPAN_INPUT_ON_BOARD_MIC); // use the on-board microphones
  //myTympan.inputSelect(TYMPAN_INPUT_JACK_AS_MIC); // use the microphone jack - defaults to mic bias 2.5V
  myTympan.inputSelect(TYMPAN_INPUT_JACK_AS_LINEIN); // use the microphone jack - defaults to mic bias OFF

Does that make it work for you?


As an unrelated issue, I see that you’re asking for an FFT size of 512. At the same time, I also see that your audio block size is at 32. I know that this might sound weird, but these two settings do have some interaction. For example, I’ve only ever run the system when the FFT size is 2x or 4x the block size. So, for a block size of 32, I’ve only ever used an FFT size of 64 or 128. It’s possible that 512 will work with a block size of 32, I’m just letting you know that I’ve never tried it myself. You’ll find out!

If you do have trouble, try increasing your block size up to 128. Since 512 / 128 is 4, that means that it is a 4x case, which means that it’ll be more akin to what I myself have used.

Either way, let me know…we want to help it work for you!


I listened to the code using your N=512 along with the audio_block_length being 32. To my ears, it ran fine despite the FFT being 16x the block size. Fantastic! You just did something new!

What I did hear, however, is that the processed audio had a short, but noticeable, delay relative to the original sound. It felt like a slight echo. This kind of latency is one downside of using a long FFT (N=512) with a slow sample rate (16 kHz). At minimum, latency is the FFT length divided by the sampling rate, which in this case is 512/16000 = 32 msec. In reality, the latency will be more than that…possibly up to twice that value.

If you want to reduce the latency, you might consider running at a faster sample rate (24 kHz? 32 kHz? 44.1 kHz?), or using a shorter FFT, or both.

Hi Chip,

Thanks for taking a look. This indeed took care of the first error. Did you try starting the SD recording? After changing the order of the commands still produces a ton of errors like this when you activate the SD recording:

AudioSDWriter: chan 0, data skip? This ID = 0, Previous ID = 13570355 AudioSDWriter: chan 1, data skip? This ID = 0, Previous ID = 13570355 AudioSDWriter: chan 0, data skip? This ID = 0, Previous ID = 13570355 AudioSDWriter: chan 1, data skip? This ID = 0, Previous ID = 13570355

Just for giggles I changed the block size to 128 and I got something slightly different. It repeats this error over and over again with the exact same numbers.

AudioSDWriter: chan 0, data skip? This ID = 9835, Previous ID = 9510
AudioSDWriter: chan 1, data skip? This ID = 9835, Previous ID = 9510
AudioSDWriter: chan 0, data skip? This ID = 9526, Previous ID = 9835
AudioSDWriter: chan 1, data skip? This ID = 9526, Previous ID = 9835
AudioSDWriter: chan 0, data skip? This ID = 9761, Previous ID = 9526
AudioSDWriter: chan 1, data skip? This ID = 9761, Previous ID = 9526
AudioSDWriter: chan 0, data skip? This ID = 9510, Previous ID = 9761
AudioSDWriter: chan 1, data skip? This ID = 9510, Previous ID = 9761

I updated the pastebin… got rid of a couple superfluous things.

I opened up the .wav file and it seems to be mostly okay despite the errors? Maybe a popping noise or two? I haven’t experimented with it too much. So really the worst thing that’s happening is dumping tons of error messages to the serial port. Is there an easy way to suppress those? (short of just commenting it out in the library)?

If you don’t mind I have a couple more questions.

  1. What is the block size (e.g. 128 instead of 32) doing in the library and why would you pick a smaller/larger value? (I was simply fiddling with the knobs, not doing anything intelligent.)

  2. Do you have any good rules of thumb for the AudioMemory setting? When I was fiddling I had to use larger numbers for larger FFTs, but I was wondering if there was any more intelligent way to think about it.

Thanks again!

I do NOT understand why I don’t get email notifications of activity. So annoying! And it leaves you hanging with hot and exciting questions! Sorry!

Regarding the “errors” during SD writing…

  • I wouldn’t characterize them as actual errors, but as opportunities when there might have been an error. It’s actually quite difficult to tell if the SD card itself has hiccupped and caused some audio to be dropped. [If you’re not aware, an SD card is itself a tiny little computer, which can take a non-deterministic amount of time to actually respond to a request for a write. That’s why it’s important to get a good card.] In trying to figure out if audio blocks might be dropped, I kludged together a pretty amateur approach. Clearly, when the “This ID” is stuck at zero, something is amiss with my amateur approach.

  • The second set of errors look somewhat more troubling as those numbers are actually close enough to maybe reflect that audio blocks have been dropped. With the second set of errors, how many errors are you getting (per minute? per 10 minutes?)? Just a handful, or lots and lots and lots? If it’s lots and lots, it’s clearly not capturing dropped audio…I’ll see if I can figure out how to suppress the errors.

  • Regarding the quality of your SD card, did you provide your own SD card, or did we provide you with one? For example, is it a SanDisk Ultra (or SanDisk Extreme) or something else?

As for rules-of-thumb regarding the AudioMemory setting, you need enough so that your algorithm blocks don’t run out. That’s not terribly enlightening, so here are more thoughts:

  • Usually, if I’ve forgotten to allocate enough memroy blocks, the audio sounds horrible (glitchy with breaks and drop outs). If it sounds fine, you’re probably fine.

  • If you want to get quantitative, many of the example Tympan programs have CPU and Memory reporting that show how many of AudioMemory blocks are being used (as well as how much free RAM is available after the audio memory has been allocated). If it reports that you’re using fewer audio memroy blocks than you’ve allocated, you’re good! There’s no need to have more than a handful more than the max that you’re actually using.

  • As an aside, the SDWriter has it’s own buffer of memory to help smooth over the non-deterministic write time. That buffer is separate from the AudioMemory pool (though it should be reflected in making your FreeRAM smaller), so that shouldn’t come into the calculus of what value to use for AudioMemory.

  • If the audio processing blocks were all perfect in their ability to manage and release memroy, and if you weren’t doing FFT processing, you should always be able to get away with less than 10 blocks of AudioMemroy. If you are doing FFT processing, you’ll need more…you’ll need enough AudioMemory to handle the buffering that happens as part of the FFT overlapping. For example, for 50% overlap, you need 2 blocks per left/right channel for buffering. For 75% overlap, you need 4 blocks per channel. For 87.5% overlap you need 8 blocks per channel, etc.

I don’t think that I’ve ever actually needed more than 40 blocks. But, the Teensy 4.1 that is at the heart of your Tympan RevE has so much RAM, that you could easily allocate 150 blocks of AudioMemory and not pinch your RAM usage for other tasks. So, for an embedded processing device (ie, not Matlab on your PC) you have plenty for most anything that you want to do.

Did I address what you asked?

Thanks for sticking with it!

Chip

Hi Chip,

Unfortunately that error message with the differing block numbers comes up constantly. When you play it back it doesn’t sound too weird so the occasional glitch seems ok if you’re not using the file for something precise. I think I’m using the SD card it came with - a SanDisk Ultra. (I’ve had struggles with lower quality SD cards in other applications (hello Raspberry Pi) so I’m sympathetic.)

We got some FFT processing to work with 50 memory blocks and that gets us N=2048 FFT bins. For a while I was getting some nasty reboot loops if I chose too small a value for the audio memory.

Thanks for your help thus far, it’s been a big lift to our team. Any thoughts on why you would choose a different block size from 128 (which I understand is the practical max for the platform)? I’m not quite sure what exactly the block size is doing.

The block size defines how often the system does a round of audio processing. So, with 128, the system waits for 128 samples to be acquired (per channel) and then invokes the full chain of audio processing (whatever you’ve programmed) to be applied to that chunk of audio.

Long Blocks: Since there is some overhead each time the audio processing chain is invoked, the system is most computationally efficient with large blocks. You can run more complicated algorithms because you are using your CPU resources more efficiently.

Short Blocks: But, with long blocks, you have to wait for all those audio samples to occur before you start processing. A long block of audio could represent 10s of msec of time. That ends up being the (best case) delay between sound hitting the mic and processed sound coming out the speaker. That latency is bad for human listeners. It sounds like an echo and degrades intelligibility. If low latency is required, the shortest blocks are best. It’s a trade-off.

Current Limits: Without taking extra steps to change some deep down code, 128 is the max allowed block size. For the minimum, you could run a block size of 1 sample, but I’ve never run less than 4 samples.

FFTs Complicate Things: For FFT based processing, there’s the extra constraint that the block size, the FFT size, and the FFT overlap become interdependent. Basically, you can only pick two of these values freely. Example:

  • You want a 256 point FFT with 75% overlap. That means that you are computing a new FFT envery 256*(1-0.75) = 64 points. Therefore, your block size should be 64. That’s not too hard.

Long FFTs: It looks like you want longer FFTs. That’s where you start to hit some annoyance. Sorry. (We can change the system for you if you are hitting these annoying limits.). Let’s say that you want 2048 point FFTs and you only need 50%. Your block size, therefore, should be 2048*(1-0.5)=1024 points. You can’t have a block size this long in the current software. Ugh. The max block size is 128.

When you’re limited to 128, but you stick with an
FDT of 2048, it means that your overlap will be 1-128/2048 = 94%. You’re doing way more calculations than you need to. That’s a recipe for chewing up all your CPU cycles quickly.

Moving Forward: If you’re doing long FFTs, it’s because you want fine frequency resolution? If so, two ideas:

  • You can get similar resolution with shorter FFTs by slowing down the sample rate. Slower sample rates cut out high frequency audio but maybe you don’t care? If you can cut the sample rate, the resolution you get with 2048 points at 44.1 kHz can be had instead as 1024 points at 22.05kHz. that’ll cut your math load in half.

  • or, we can work together to mid the library to increase the max block size. It’s not hard, but it will be a special case for you. If you’re ok with that, I’m ok with that. This would let you keep long FFTs, a high sample rate, and reduce your overlap so you aren’t doing all that unneeded computational work.

Chip
(written on my phone…sorry for garbled text)

Regarding the memory setting. I think that you should just set it at 100 units so that you don’t have to worry about it. I think that you’ve got plenty of RAM, so just set it big.

As for the reboots, that’s frustrating! Sorry! Here’s how I’d look at that…start from a stable working condition and walk to where you hope to be. See where it falls apart. In your case:

  • Set your code for a block size of 128 and an FFT size of 256. Then look at the CPU and memory usage (let me know if you don’t know how).

Now the fun begins:

  • Switch up to an fft size of 512. It should more than double the CPU usage.

  • Double the fft size again (to 1024). It should double the CPU usage again.

  • Do you have any headroom to double it again to 2048? Have you run out of cpu? Is that why it’s resetting?