Real Time Audio Processing

Hello,

I had a question regarding real time audio processing. Im trying to create a project that takes in a 3.5 mm jack/ and or mic input to A0 coverts it through ADC and outputs it on the audrino pins of the DAC. I have already created a preamp that takes in a mic into A0 as well as a 3.5 jack.

Furthermore Ive created a R2R resistor ladder with a low pass filter, power amplifier, and output. I know the R2R ladder can be low quality. Ive inserted an image of my dac circuit below. As well can be seen in this link https://www.instructables.com/id/Arduino-Audio-Output/

However my question is there a way I can code to take the audio and output it to the R2R resistor ladder I am fairly new to this and any help is greatly appreciated. Thank you! (preferable register programming help rather than libraries)

Unless you use resistors that are matched to within 0.2% of each other than that circuit will cause all sorts of distortions in your already poor audio signal.
It is poor because you have a limited sample rate.
You will need to use the direct register addressing to write to them and you will need more than one access because you do not have a clear run of 8 bits free in any register used with an Arduino. This will cause additional glitches in your sample.

But your big problem is lack of memory for the processing code. Unless you can add some external memory what you could do will be limited.

The AVR microcontrollers used in Arduinos are not suited for audio processing.
They were simply not designed with audio in mind: they lack memory, processing power, ADC resolution and speed, and a DAC.

Use a microcontroller with more memory, I²S support, DMA, etc.
Take a look at ARM microcontrollers like STM32 if you have some experience, or use a Teensy if you want a more beginner-friendly platform.
You could probably also use an ESP32.

If you're interested in learning low-level DSP, the CMSIS-DSP library might be useful.

If you don't care about DSP at all, and you just want to get your audio project working, you can use the PJRC Audio library.

freedsp.cc is also worth a look.

Pieter

You can't parallel opamps like in your circuit, if you want more current than one opamp can supply there is
a way to do it: https://e2e.ti.com/blogs_/archives/b/thesignal/archive/2013/03/26/paralleling-op-amps-is-it-possible

The resistor ladder needs most precision in the resistors closer to the output end, note.

With tight coding you can do this audio processing to some extent, you'll need to up the ADC clock speed a bit
I think, and restrict the processing to cheap operations. An external SPI ADC can be a lot quicker to read
(compared to waiting for the on-chip ADC), freeing lots of processor cycles.

You can even do noise-shaping to increase the effective number of bits on the output for lower frequencies,
but you need a high sampling rate for this to be effective.

This I2S library allows the Arduino DUE to interface with a large number of audio codecs and enables higher quality audio I/O than what is available with the standard on-chip ADC and DACs.

To make use of the on-chip ADC and DACs plus a real time digital filtering, there is this tutorial (google translate is your best friend).

MarkT:
The resistor ladder needs most precision in the resistors closer to the output end, note.

No, think about it. To achieve monotonicity all resistors have to be of the same precision.

zkhan:
However my question is there a way I can code to take the audio and output it to the R2R resistor ladder [...] (preferable register programming help rather than libraries)

Not sure why you want to an audio passthrough with an Arduino (besides a proof of concept maybe), but it's not that difficult:

void setup() {
  DDRD = 255; // The whole port as an output
  // ADC settings that speed it up and I don't remember the register name.
  TIMSK0 = 0; // Improves "sampling rate" a little more, but it's optional (since this disables delay and time functions)
}

void loop() {
  PORTD = analogRead(A0) >> 2;
}

Since this is a simple passthrough, precise timing of the sampling is not that important. It may go as fast as it can.

Grumpy_Mike:
No, think about it. To achieve monotonicity all resistors have to be of the same precision.

I did think about it, the resistor precisions are weighted by bit position, a 10% resistor for the LSB is
a smaller deviation than a 0.1% resistor in the MSB for an 8 bit ladder.

Thank you for all you helpful responses. Any tutorial or simplest way I can accomplish this task. Tutorial maybe. Thank you.

MarkT:
I did think about it, the resistor precisions are weighted by bit position, a 10% resistor for the LSB is
a smaller deviation than a 0.1% resistor in the MSB for an 8 bit ladder.

No the contribution of the most significant bit must be as accurate as the least. Because 0111 1111 must be lower than 1000 0000

Thank you for all you helpful responses. Any tutorial or simplest way I can accomplish this task. Tutorial maybe. Thank you.

What is "this task"? You didn't say what quality/resolution you want and you didn't say what kind of audio processing you want to do.

I think some people have made simple-limited guitar pedal effects... But, the Arduino is not going to do anything high-fidelity or highly-complex. (And DSP tends to be mathematically complex unless you're something simple like volume manipulation.)

You might want the [u]MiniDSP[/u] or the [u]Dayton DSP[/u] ?

zkhan:
Thank you for all you helpful responses. Any tutorial or simplest way I can accomplish this task. Tutorial maybe. Thank you.

The simplest way is to put aside the Arduino and connect the output of the microphone preamp to the input of the speaker amplifier.

What do you really want to do?

I already posted a link to the Teensy Audio library, it's not going to get easier than that.

Sorry for bad english. Like there is no other way to create a program with the implementation above. (R2R ladder with A0 preamp).

For example using the ADC of the audrino and a DAC Tutorial to output an analog signal from A0

OK forget the D/A and try this:-
http://interface.khm.de/index.php/labor/experimente/arduino-realtime-audio-processing/

I had a question, so I was able to implement the code below with the specification i said above. However the static noise is very loud. I already have a low pass filter as stated up in my specification. Anything i can do to reduce the static.

void setup() {
  //
  cli();
 
  //
  TCCR2A = 0x00;
  TCCR2B = 0x00;
  TCNT2 = 0x00;
  OCR2A = 249;
  TCCR2A |= (1 << WGM21);
  TCCR2B |= (1 << CS21);
  TIMSK2 |= (1 << OCIE2A);
 
  //
  sei();

}

ISR(TIMER2_COMPA_vect) {
  //
  analogWrite(10, map(analogRead(0), 0, 1023, 0, 255);
}

void loop() {
 
}

By static do you mean aliasing/quantization noise? Do you have a sample of this to listen to?

What frequency is timer2 running at?

MarkT:
What frequency is timer2 running at?

Assuming the typical 16 MHz clock frequency, then the interrupt is firing up at a rate of 64.25 KHz (every 15.56 microseconds).
Although with the ADC's default prescaler, effective sampling rate is more like 10 KHz or even less. Successive approximation ADCs aren't known for fast conversions; so in order to achieve high sampling rates, their clock shouldn't be too slow.

PD: the map() function is rather slow also. The CPU is 8 bits "wide" (against the 32-bit variables it uses inside), and its ALU lacks integer division support (such operation is actually done in software, and the function needs it).
Since you're scaling two power-of-two-wide ranges, the fastest way to accomplish the same result, is to move around some bits. Narrowing 10-bit values into an 8-bit input is possible simply by discarding two least significant bits (aka right shift 2 bits). In the end, it's something like this:

analogWrite(10, analogRead(A0) >> 2);

Furthermore, you may even want to change the duty cycle quicker. Instead of analogWrite(), you can set a single register to achieve the same effect.
Pin 10 belongs to the channel B of the timer1, so the correct register is called OCR1BL (ends with 'L' because that's a 16-bit timer and you only need the LSB to set the duty cycle). Improving that line even more:

OCR1BL = analogRead(A0) >> 2;

I'm suggesting all this because 15 microseconds is a quite tight time budget for an AVR microcontroller, thus having to do everything as "low level" as possible.

PD 2: I've just realized another problem: you're using a timer1 output, which defaults to a prescaler of 64 and runs in "phase-correct" mode; resulting in a carrier frequency of just 490 Hz. I don't think you'll manage to modulate like that any meaningful audio signal, do you?

SAR converters can be fast, but not so much the one the ATmega chip...

(I know this because I'm just starting to play with an ADS8885, 400kSPS 18 bit SAR ADC chip with 1.2µs
settling time to full precision...)

I don't think you'll manage to modulate like that any meaningful audio signal, do you?

I can actually have really bad quality signal come out of it. You can make out the sounds and words a little bit. And thank you for the insight. Furthermore, is there a way I can increase the quality of the sound in a low level aspect? Thank you!

8bit R2R DAC can generate decent sound and you should definitely make out the sounds and words. Here's a reference of 8bit DAC playing audio on Arduino Uno and this doesn't even have any filter, just resistors: Arduino Uno Jamming and Blinking - YouTube

You need to eliminate the issue in the components you have. E.g. is your DAC actually producing proper signal. If you don't have oscilloscope, make little program to output sine wave for example and listen do you get proper output. Is the analog input working? With silent sound what's the value. How does the range of values change as the sound gets louder (record min & max). Is there clipping when you direct input to output?