Questions about digital noise and grounding

Yes, these are good advices. My capacitors were 100 µF, but I’ll try with larger capacitors too.

Anyway here is my noise gate code. All the instructions are in comment in the main.cpp file, but I’ll reproduce them below too:


Noise gate example and calibration application for the Electro-Smith Daisy

NoiseBleach is an 8-band noise gate. It is some kind of intermediate solution between a standard noise gate and an FFT-based noise removal. It has more or less the best of both worlds.

Each frequency band has its own threshold and gate time. The former can be configured at any time, and the latter is automatic.

Even if the default configuration works, you should take the time to calibrate the gate and find the optimal values for your hardware settings.

Compile with:

C_INCLUDES ?= -I.
C_DEFS ?= -DNDEBUG
OPT ?= -O3

You may have to add CPPFLAGS += --std=c++14 too, but this should work without it on any recent compiler.

The code makes intensive use of debugging assert(), so remove them with -DNDEBUG in production. A single channel uses about 14% of the CPU with a block length of 48 samples. You can reduce the number of bands to save CPU, but this will lower the noise efficiency.

This class was initially part of a larger code base, hence the weird dependencies I tried to group and minimize. It’s not made for Daisy but the portable parts including the DSP bits work fine, or require minute adjustment.

Calibration procedure

You’ll need:

  • The regular audio source for your setup (a guitar, a console output, whatever). Of course you can do it without, it’s just that it won’t take the source noise into account, only the Daisy environment.

  • Something to record the output of the Daisy. No super spec is required, just the bare minimum.

  • An audio file editor to check the recording, preferably with a spectral display (it’s easier but not mandatory at all)

  • And of course your Daisy hardware.

First, set the #define CALIBRATE_NOISE_BLEACH after the #include list in main.cpp. Compile and upload the program to your Daisy.

Run the test with an instrument plugged to the Daisy Seed left input. It should remain as silent as possible (put a sock between the strings of your guitar) but not muted at the volume pot.

Record the output and press the Reset button to start the run. Wait for 4 minutes and stop the recording.

Open the file in your audio editor. For simplicity, trim the file at the reset “plop”. This will be easier to measure the time offsets.

The 8 frequency bands are swept in ascending order, one after each other. They starts from -144 dB and the threshold is increased by 6 dB every 2 seconds. There are 15 steps, so this is 30 s per band. Normally, each band should start at full volume. At a given time, it starts to fade out step by steps, then finally becomes silent.

For each band, find the moment where it becomes silent. It’s easier to check with a spectral display rather than a waveform. Subtract this moment from the band time offset. Divide by 2 this duration in seconds to get a step index, multiply by 6 to get a dB value, then subtract 144. This is your threshold level in dB. Then convert it to a linear scale.

step      = (time_silence - time_band_beg) / 2
level_dB  = 6 * step - 144
level_lin = pow (10, level_db / 20)

Once you’ve done this for the 8 bands, report the value in the nzbl_thr_arr array which is used to initialize the NoiseBleach objects. You can tweak the value at your taste to let pass a bit of noise and increase slightly the sustain.


I also have a class called NoiseChlorine, working the same way but with weaker filters, and lower on CPU load. If anyone is interested, I can share it too.

1 Like

Maybe you should add a line filter/blocking/choke inductor in series of the + powering the display.

With the capacitors and inductor you get a second order low pass filter (not a bandpass filter! the capacitor and the inductor should never be put in parallel). Maybe add a resistor to avoid overshoot/resonance depending on the sqrt(L/C)

Note that big capacitors have internal resistance that limit their HF efficiency.

I did some more tests. Indeed, a large (1000 µF) cap on VIN helps a lot, but we’re still far from the goal. Adding a 100 nF ceramic cap as close as possible to the Daisy pins improves the noise performance from a tiny bit in the high part of the spectrum. There is no effect with a cap on +3V3_A, and for some reason things get worse with a cap on +3V3_D. Also, I’m still on a breadboard, so these results may become different on a PCB with real ground planes.

2 Likes

I’ve used a separate power supply for external devices, so now the Daisy Seed gets a dedicated power source. The noise improved quite a bit, but there are still high-pitched noise when drawing frames for the LCD. And it gets worse when I run Reverbsc with the display. Maybe the processr itself emits noise when it’s busy running particular tasks. :thinking:

About beads/inductors.

Re. some of the highpitched digital noise. The STM32 chip is actually a little too good at regulating its own power consumption. So there is a transient in current draw when the doing periodic things (like processing audio in the callback). This causes a slight lift in the GND signal that can create this digital noise (most audible at the callback frequency).

A bandaid that works when doing fairly trivial DSP is to increase the callback frequency to something outside of, or at the upper limits of human hearing. Obviously this is not always possible.

What we did on the Petal, and Field hardware (similar to on the Patch, etc.) is to use a DC-DC Isolator to create an isolated 5V supply that powers the Audio parts of the circuit (Buffers in this case). The daisy is then powered from 9V directly, keeping any noise on the 9V power brick instead of the audio path. (On the Daisy Patch we did the opposite – powering the Daisy Seed from an isolated supply, while the rest of the circuit and eurorack power stays clean).

The PDS1-S5-S5-M has worked well for this (5V-5V isolator), as has the PDS1-S24-S5-M (24V-5V isolator – used on the Daisy Patch)

The circuits for both the Patch and Petal are up on the Hardware Github Repo

6 Likes

“A bandaid that works when doing fairly trivial DSP is to increase the callback frequency to something outside of, or at the upper limits of human hearing. Obviously this is not always possible.”

Actually this is what I always do on other processors - I calculate the latest DAC samples at 96kHz and have a counter that increments by one to choose what else to do during the sample. Divide by 8 and a Case statement usually works fine.

And I was wondering for a second why this code looks so familiar…
It’s surreal bumping into you here. I’m still using your FFT anytime I need one, it’s the most usable implementation out there.
Looking forward to trying your noise gate.

1 Like

To try and move the 1kHz noise (from 48kHz sampling and blocksize = 48) above audible frequencies it is pretty easy to lower the blocksize. Is anything bad going to happen or degrade if I make the blocksize 1 of 2 or something super low? Maybe processing will just go up? How was 48 chosen to be a good blocksize to start with?

The 48 blocksize was chosen as a way to have an easily understood callback rate (48000Hz / 48 = 1000Hz – or 1ms), that can still benefit from block processing while maintaining minimal latency.

Any effects from reducing blocksize would largely depend on how busy your process loop is, as well as how much stuff you need to do outside of the callback. Whatever is happening in other interrupts, or in the main infinite loop will be getting interrupted more frequently. This most likely would have a negative effect on stuff like USB or SD Cards more than anything else.

But if all you’re doing is generating audio then there may not be any issue.

3 Likes

I’m having some trouble with the noise issue as well. I’ve designed a eurorack module around the Seed, and have incorporated the isolator and caps in almost exactly the same configuration as the Patch. I was also able to reduce the block size down to 10 in my code without problems, but that still puts the whistling noise at 4.8kHz. Both of these things helped a lot, but I still find that I need to apply a 10dB cut with a Q of 18 in Ableton to make the noise inaudible.
Does the problem stem from the fact that the audio codec uses the same VCC and ground pins as the MCU? I wonder if using an external codec with better supply trace routing might solve the issue?

That’s one solution. Other is to try to get the block size down to 1 by dividing your code up into segments that are spread over 8 or 16 callbacks.

oh that’s interesting, I didn’t think of that. My code has a combination of delay lines (reading from the SDRAM), filters, cross fades, ADC reads, digital outputs etc, so if I spread those around a number of callbacks the processor might be able to keep up? Presumably I would need to read the audio input on the first callback, and then end up writing the output on the last?

Yes it’s almost the same amount of code, you just add a counter from 0 to 7 or 15, increment it each callback and then test it and run that portion of the code. Just divide it up into 8 or 16 functions. If you can keep each function of similar time - the better you do this the quieter it will be.

You still read the audio in and write the audio out every callback. You get rid of the buffer (set it to 1)

I’m not quite sure how that would work - if I read and write the audio every callback it won’t have calculated all the data for the output? I mean it’s ok for dry signal, but for the wet signal (my code is a delay effect) won’t it run in to problems?

You’ll still have to buffer data. Say you decomposed your processing function into 16 balanced sub-functions. The time can be divided in cycles of 16 samples:

Time 0 — the ADC provides the 16th sample (index 15) of a nearly full buffer, let’s call it A. Then you can start the processing calling function 0 on this 16-sample buffer. On the output, you’ll pass the sample at the index 1 of the previous output buffer, let’s call it B.
Time 1 — start filling a fresh input buffer (let’s call it C) with the input, index 0. Process the buffer A with function 1. Send index 2 of the buffer B to the DAC.
Time 2 — add the input to the buffer C, index 1. Process the buffer A with function 2, send buffer B index 3.
Time …
Time 14 — add the input to the buffer C, index 13. Process the buffer A with function 14, send the last sample of the buffer B, index 15.
Time 15 — add the input to the buffer C, index 14. Process the buffer A with function 15, and now it is ready for the output. Send index 0 of this buffer to the DAC.

Then repeat this cycle, rotating the buffers.

2 Likes

If you think of it you have to calculate 48000 samples per second no matter how you do it, whether in blocks of 1 or blocks of 16. So the real time stuff should be done every callback, calculating just the latest value. But you also have non-critical stuff which isn’t updated that often - reading controls, USB, display, etc. This is the code you should do a little of every callback using a counter.

Alternatively it is often slightly faster to calculate say 8 samples at a time into an array as it saves on loading pointers, etc, using one or more of these callback routines for this.

But you should just load in one sample from a saved array at a time into the buffer so as to avoid any repetitive burst of noise.

In fact you can load two samples at a time as noise will be at 24kHz if you prefer, but that often makes the code more complicated, not less.

3 Likes

Ok just as a quick update - I did as MikeDB suggested and spread the non-realtime processes out. I put them into 7 different functions in the end, and used a switch statement and counter to select one per callback. I left all the audio processing code alone, and set the block size to 1. Everything functions as it did before (well I had some LPF’s on the control inputs so these need to be tweaked), with zero whistling in the audible spectrum. Worked like a charm! Thanks for the help guys!

1 Like

ok slight edit - the whistling is still in the audible spectrum, but at a much lower level. I tweaked it to be in 4 functions, and the whistling is now at 12kHz, but it’s not audible over the hiss from the opamps / codec etc.

2 Likes