Questions about digital noise and grounding

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!

2 Likes

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.

3 Likes

Glad you got it working. It’s a common problem all these programmable audio platforms. Linux is even worse as you can’t make ALSA work with a block size of less than 10 so even sampling at 96kHz it’s audible.

However you shouldn’t be getting much hiss from the CODEC and op-amps. How loud are we talking about ?

Oh the hiss is pretty quiet, I had to boost the already healthy signal level by 24dB in Ableton to be able to hear it! Some of that will be my interface as well.

Okay that’s great.

BTW if you (or anybody else doing the same) have any spare time left after any of the non-realtime functions in that 1/12 mS timeslot, you can measure it and put a random delay at the start of the function with 0 as minimum and just under that spare time as maximum, changing the wait every cycle. This dithering spreads the noise out rather than being a repetitive 12kHz tone.

oh neat, yeah I’d like to try that. How would I go about measuring the time taken for the functions to execute?

I run counters at the end of all my functions when they are waiting for the next callback, then the callback records those values and zeroes them.

while {} timefunc1++;

And in the callback :
savetimefunc1 = (timefunc1 + 1023 * savetimefunc1) >> 10;
// this converges to the average wait time pretty quickly
// also good to record the maximum
if (savetimefunc1max < timefunc1)
savetimefunc1max = timefunc1
timefunc1 = 0;

Of course this depends on the callback being interrupt driven - you’d need to check that is how Electrosmith have done it. If it’s a round-robin scheduler it will just hang up.

I use the Arduino environment and i get some digital noise when connected to the PC (you know the kind of noise you get when you can hear the mouse when you move it).

When the Daisy Seed is connected to a power bank i do not get any digital noise.

Is there something special in the Arduino implementation that makes this noise disappear ? (constant charge of cpu ?..)


my test consists of a filtered osc with osc.SetAmp(0.0003f) with svf → EIEpro soundinterface with input gain set to max for line input (~50dB).


[EDIT]

  • I added a “USB silencer” (galvanic isolator) and connected the daisy through it to the PC.
    Now i have the 1kHz and its harmonics (it’s not drown/hidden by the PC digital noise) as described in this thread. It is at about -80dB (compared to a full scale sine)
  • The noise still disappears when connected to a powerbank

It would be interesting to see how other power sources can affect this noise.

I appreciate the all discussions in this thread, really good stuff.

Related question: How much of a difference does a four layer pcb make for the audio noise floor? If you design smart enough on 2 layer, is 4 layer worth it?

Massive !!!

4 layers used to be prohibitively expensive so we spend weeks avoiding it unless we had to. But nowadays it’s well worth considering. Are you talking production or one-offs. If one-offs then it’s a no-brainer - 4 layer prototypes at JLCPCB are SO cheap it’s untrue.

2 Likes

Here is a link to a discussion describing how this very same problem is efficiently dealt with on the Axoloti. (specifically the answer by Johannes, the creator of the axoloti where he describes the problem and the option he chose)

I ordered some TPA6132 breakout, i will let you know if these can help.

1 Like

I updated my code in post 5 to reflect changes in libDaisy. It seems there is a time limit to edit posts (it would be great if it could be removed) therefore I couldn’t update the link, so I just changed the linked zip content, still showing “r1” in the filename.

Thanks for your input. That’s what I was hoping to hear! I’m starting off with a rev1 small batch of say 3-4, then rev2 I’m shooting for 12. After that who’s to say how many, only time will tell.

I don’t think that addressing this hardware issue by constraining/fragmenting software execution is a good solution.

5 Likes

Well with those volumes go for 4 layers so it definitely works. Then if the product really takes off and you want to squeeze every last cent out of the cost try a 2 layer version to see if it can meet spec. It is possible, but I bet you have to recycle the PCB at least once. Assuming it’s SMD, remember to lay the planes correctly - 1 = components, 2 = GND, 3 = power distribution and the odd connection, 4 (back) = connections between vias. Don’t just throw thousands of GND vias everywhere - decide where best to ground each part of the circuit to the inner ground plane so you minimize circulating currents in the inner ground plane.

1 Like

Today i tried a basic “ground loop isolator” between the daisy audio output and my sound card. It efficiently removed the 1kHz buzz.

1 Like

I’m surprised the buzz wasn’t noticed before these went into production.

3 Likes

I had a similar issue with my Nord modular G2 engine.

It seems that it is the classic ground loop issue.

My solutions so far:

  • 5R1 resistor between DGND and AGND (reduces digital noise by 15dB)
  • disconnect daisy from PC USB when i’m not programming it and use another power source.
  • use a “ground loop isolator” on the audio output.

I guess that the last solution is the best one.

Using an USB isolator (iSilencer) did not solve the problem for me.