Grab audio-in to be sent over the Serial/USB link and played on a PC

Hi there,

I'm an Arduino beginner and just started to practice with the Arduino Starter Kit suggested projects.

Everything looks so easy and simple if you deal with making LEDs blink or playing tone with the tone() function, things become a lot more complex if you try to sample real audio and try to elaborate it for other porpuses.

I'd appreciate if you can give me some hint about the following basic concepts.

My idea is to grab audio input, from 2 or 3 analog pins and send the samples over the USB/Serial port to a PC.

To do that I read analog audio from an input jack (a microphone or iPod will be plugged to the jack) with the analogRead function. Since the voltage of the input oscillates in the range of few millivolts I guess I'd need to process (scale) a little bit the signal with an OpAmp (I've found a pretty simple circuit sketch of a LM386 OpAmp) in order to get more appreciable samples to be mapped to the 0 to 1023 bit of the internal ADC of Arduino.

First question

What I read (the 10 bit values) is "noise-free"? Whatever I've read online (Arduino forum and others) about managing audio, assumes that you prescale the timers/clocks of the microprocessor in order to avoid/remove "digital" and DC offset noise. Is this mandatory or can I just use the standard delay() function to store my samples? I didn't get if the digital/DC noise is only a problem if the audio has to be PWModulated back from a digital Pin!!!

Second question

What I read is the result of ADC, so it's already digital audio. Are this samples RAW audio? Is it sufficient to translate raw to .wav format (or so) if I want to play the audio from the PC? I don't need to use PWM in order to "translate" my samples into a RAW audio format, am I right? PWM is a DAC funtion and in case of audio playing, is required if my audio is to be played back to a speaker out from a digitalPin, right?

Third question

Can I read as much as analogPins I want simultaneously (let's say 2-3), to store samples into several variables?

Thank you.

Simone

I can't answer your main question... I think the regular FTDI serial port mode for the USB is too slow for real time audio. It's way-way too slow in the default Serial Monitor mode.

My idea is to grab audio input, from 2 or 3 analog pins and send the samples over the USB/Serial port to a PC.

The ADC is a little slow for high quality audio. I think it can sample at 22.05kHz, but it can't sample accurately at "CD quality" 44.1kHz. And as you know, it's only 10-bits.

To do that I read analog audio from an input jack (a microphone or iPod will be plugged to the jack) with the analogRead function. Since the voltage of the input oscillates in the range of few millivolts I guess I'd need to process (scale) a little bit the signal with an OpAmp

A microphone needs a preamp. An electret condenser needs power.

A headphone or line-level signal is around 1V (with the volume turned-up) so you don't need a preamp.

The Arduino cannot read the negative half of an audio signal. You need to [u]bias[/u] the input at 2.5V. You can then subtract-out the bias in software if needed.

(I've found a pretty simple circuit sketch of a LM386 OpAmp) in order to get more appreciable samples to be mapped to the 0 to 1023 bit of the internal ADC of Arduino.

The LM386 is a "power amplifier" designed to drive a speaker. [u]Here[/u] is the schematic for the SparkFun microphone breakout board. It has the power for the electret mic and it has biased output

If you want to build your own preamp you can start with that circuit. Then, you can change the gain, or make variable gain, etc., if you wish. You can also use almost any op-amp.

What I read (the 10 bit values) is "noise-free"? Whatever I've read online (Arduino forum and others) about managing audio, assumes that you prescale the timers/clocks of the microprocessor in order to avoid/remove "digital" and DC offset noise.

I'm not sure what they are trying to say... There is no inherent noise in the ADC.

However, there is quantization noise in any digital signal. Quantization noise rides on top of the signal so it's most noticeable with quiet sounds, but there is no quantization noise during "digital silence". If you want to know what quantization noise sounds like you can use an audio editor (such as Audacity) to downsample an audio file to 8-bits. It won't be as bad at 10-bits, and you can't hear any quantization noise at 16-bits (under normal/reasonable listening conditions).

And in order to prevent aliasing (false frequencies) You also need to filter-out any frequencies above the Nyquist limit (half the sample rate) before digitizing.

Is this mandatory or can I just use the standard delay() function to store my samples?

The problem with delay() is that it pauses the microcontroller during the delay period. So, you won't be able to do anything else.

DC offset noise.

The DC offset isn't "noise", it's the same as the bias I mentioned above. For example, with the input biased at 2.5V (half the 5V supply) silence will read 512 and the audio data will bie biased/offset at 512. You simply subtract 512 to get the unbiased audio data. Sometimes the digital-to-analog converter needs to be biased anyway, and in that case you can remove the DC voltage bias with a capacitor.

Are this samples RAW audio? Is it sufficient to translate raw to .wav format (or so) if I want to play the audio from the PC?

Yes, you'll have "raw" PCM data. A WAV file is basically RAW data in a file preceded by a header. The header identifies the file as a WAV and it gives the sample rate and bit-depth, and some other information so the audio data can be played back properly. You can research the format of a WAV file.

PWM is a DAC funtion and in case of audio playing, is required if my audio is to be played back to a speaker out from a digitalPin, right?

PWM is not analog. It can approximate analog if the PWM frequency is much higher than the audio frequency, and if the PWM is low-pass filtered.

Can I read as much as analogPins I want simultaneously (let's say 2-3), to store samples into several variables?

No. The ATmega chip has only one ADC with multiple-multiplexed inputs. If you can figure-out the timing, you might be able to compensate for the delay between reads and re-align the waveforms.

Wow! Lot's of info! Thanks DVDdoug! :-)

It'll take me some time to "postprocess" them and do some practice with some test circuit/code to let them settle... anyway, I've some questions/considerations.

1- Ok biasing the voltage, understood! Except for the 10uF capacitor... what is exactly the reason why it has to be polarized? (Capacitors drives me crazy!!! :-/)

2- Quantization noise, according to my understanding, is something that you cannot avoid in ADC... anyway is something that can be reduced by simply increasing the sampling frequency, right? For a basic prototype, I believe It's something that I could not worry much, right?

3- Aliasing and Nyquist, on the other hand, is something that I've studied more the 25 years ago... so I guess I'd better read something to recall what they exactly state! When you say "You also need to filter-out any frequencies above the Nyquist limit (half the sample rate) before digitizing", you mean that before the signal is read by an Analog Pin, the signal must be "low-passed" by a filter that cuts frequencies equal to 1/2 the sampling rate?

4- About microcontroller programming: delay() does something different from setting a clock and so is not the correct way to define a sampling rate, understood! So I just have to rely on the sampling rate by defining the correct value of the ADC clock by means of commands like ADCSRA to prescale the ADC timers (this is not as user friendly programming as you can imagine for the common people, but I'll try to manage somehow) and whatelse? Can I simply analogRead(), send the read value over the serial() to the USB link and do my elaboration of the sampled raw audio on my PC? Is the serial() function somehow affected by the changes of the processor clock?

Thank you.

Simox: things become a lot more complex if you try to sample real audio and try to elaborate it for other porpuses.

So very true!

Over the last couple years I've been working on an ambitious audio library for Teensy. It's pretty much an endless project, with more requests for ever-more-powerful features as more gets made. But all the basics and quite a number of fairly sophisticated things are already done pretty well.

Last year we made a nice hands-on workshop, and a 48 minute tutorial video.

As you can see if you watch the video, this is audio processing on a microcontroller implemented pretty well. Much of it depends on far more advanced hardware than you get with the low-end 8 bit AVR-based boards, but still, maybe it can serve as a reference for some of your questions about how to do audio sampling well?

Now, let's see if I can answer some of these specific questions.....

Except for the 10uF capacitor... what is exactly the reason why it has to be polarized?

Due to the physical properties of the oxide layer and electrolyte, the capacitor has a resistive conduction when the voltage is applied in reverse polarity.

You can think of capacitors are 2 parallel plates with an insulator between them. The capacitance goes up as the plates get larger, or as the distance between them gets smaller, or as the electrical permittivity of insulating the material between the plates goes up. In an overly generalized way of thinking about these things, as thinner, higher permittivity materials are used, trade-offs in performance come into play. When an extremely thin layer of oxidation is used as the insulating material, you get these polarity restrictions.

2- Quantization noise, according to my understanding, is something that you cannot avoid in ADC... anyway is something that can be reduced by simply increasing the sampling frequency, right?

Yes, this is called oversampling. Obviously it doesn't work for a perfect DC signal, since you'd read the exact same number every time. But for AC signals, oversampling can give you better results.

2 techniques are usually used to get the most out of oversampling. Filtering the results is the simplest to understand. A good digital filter algorithm can provide better performance than just averaging several numbers together (which is effectively a low-pass filter with poor performance).

Sigma-Delta modulation is the other technique commonly used with oversampling conversion. All ADC, DAC and codec chips meant for audio use this technique.

3- Aliasing and Nyquist, on the other hand, is something that I've studied more the 25 years ago... so I guess I'd better read something to recall what they exactly state! When you say "You also need to filter-out any frequencies above the Nyquist limit (half the sample rate) before digitizing", you mean that before the signal is read by an Analog Pin, the signal must be "low-passed" by a filter that cuts frequencies equal to 1/2 the sampling rate?

In practice, analog filters with steep roll-off are extremely difficult. They also tend to add distortion. Often for fairly low quality, little or no filtering is done and aliasing is allowed to corrupt the signal somewhat.

The beautiful think about massively oversampling with sigma-delta conversion is the Nyquist rate as far as low-pass filtering before the ADC is concerned is a few MHz, because it's sampling so very fast. That can capture a lot of ultrasonic signal, but it's removed by the digital filter within the converter. This, and good analog circuits without a huge digital processor on the chip silicon, are why external ADCs meant for audio give so much better sound quality.

4- About microcontroller programming: delay() does something different from setting a clock and so is not the correct way to define a sampling rate, understood! So I just have to rely on the sampling rate by defining the correct value of the ADC clock by means of commands like ADCSRA to prescale the ADC timers (this is not as user friendly programming as you can imagine for the common people, but I'll try to manage somehow) and whatelse?

If you want a reliable sample rate, you really do need to configure the ADC hardware to sample automatically, either free running or triggered by a hardware timer.

Can I simply analogRead(),

You can, but you'll have substantial sample rate jitter. Interrupts from the timers and serial and maybe other stuff cause small timing variations which cause analogRead() to sometimes make the measurements at the wrong moment, even if all the rest of your software has consistent timing (which is hard to achieve in practice).

Jitter is effectively noise on your signal. AC signals are always changing, so if you measure at the wrong moment, you get a slight different number. That's noise, even if your ADC has perfect accuracy, simply because it was used at the wrong time.

send the read value over the serial() to the USB link and do my elaboration of the sampled raw audio on my PC?

Well, that depends. Some boards like Arduino Uno have real serial and a chip with acts as a USB to serial converter. On those, you're limited by the serial baud rate. Because of start and stop bits, every byte takes 10 bit times. If you use 115200 baud, the most you can transmit (if everything works out perfectly) is 11520 bytes/sec.

Other boards have native USB. On those, the baud rate setting is ignored. The USB native bitrate is used (12 or 480 Mbit/sec). The actual data throughput depends on a lot of complex software factors, which usually boil down to how efficiently the code on both the board and your PC are written.

Is the serial() function somehow affected by the changes of the processor clock?

Yes, but the functions you use attempt to make everything "just work", regardless of the CPU clock.

Now, I would like to ask you something, and I really do hope you'll answer honestly.

Simox: My idea is to grab audio input, from 2 or 3 analog pins and send the samples over the USB/Serial port to a PC.

Lately several people have asked pretty much this same question, usually asking for an Arduino to act as a USB sound card. I've also received pretty much the same question a couple times in the last week by private email. All of these have been only about getting data sampled and sent over USB, without any mention of why, without any context of a practice project.

Is this mere coincidence?

Honestly, it's pretty obvious the PC market is flooded with cheap "USB sound card" products, so there's little practical point to using a $20+ Arduino or Teensy to build your own.

My guess is some major university or school has made this an assignment or project recently.

Really, it's fine to ask for help on academic projects. Your questions have demonstrated much more effort and cluefulness than any of the others I've seen recently. That's good.

But I really want to know what this is really for? Especially if it's a school or university, I want to know where!

Hi Paul,

thanks for helping! And thanks for the detailed and exhaustive explanations! I guess I'll need to study a little bit of sigma-delta conversion, together with Nyquist! :confused:

"Honestly answering" I'm just a private NERD! :-) A private nerd with an engineering background!

After several years of dealing with everything but circuits and code, I've recently started to play with Arduino and have had a lot of fun in building the projects of the Starter Kit!

So, I've had the crazy idea to try to build a sort of home-made PC based audio-mixer, to play with my friends! I believe that there're several products on the market that do this job better than what I'll will eventually do, but DIY is always DIY! You know!! ;-)

I hope I could ask for your support in the future again.

Thank you!

Simone.

Hi there, Can you help me this? My analogread >>> analogwrite code does nothing more than "playing", a continous " tweeeet" regardless the circuit! I says "regardless the circuit" since if I run an equivalent code based on microprocessor commands it does what is expected to do, read and write the audio played by the source (a mp3 player). I guess i'm underestimating something when sampling... the sampling is done @44.1KHz by looping a micros () procedure waiting for the next sample time (22.7)... The analogread is always reading the top 1023 value, just like i'm reading some other audio/DC signal instead of the input.

Heeeeeelp!!!

THX :-)

My analogread >>> analogwrite code does nothing more than "playing", a continous tweeeet" regardless the circuit!"

The Arduino doesn't have a DAC... analogWrite() does NOT write analog! It writes a PWM analog which can approximate analog in some applications, such as dimming an LED. You're probably hearing the ~400Hz or ~900Hz PWM frequency.

Assuming you haven't added a DAC, that's your biggest issue right now. (The Arduino Due does have a DAC.)

The PWM is also only 8-bits, so you'd need to divide by 4.... But, that doesn't matter since PWM is not going to sound like "audio" anyway...

I guess i'm underestimating something when sampling... the sampling is done @44.1KHz by looping a micros () procedure waiting for the next sample time (22.7)...

Actually, the sample rate isn't super-that critical for a simple "pass-through" as long as read & write at the same frequency. And, that's not hard to do as long as you read once and write once every time through the loop.

The analogread is always reading the top 1023 value, just like i'm reading some other audio/DC signal instead of the input.

Do you have a multimeter to confirm you are biased at 2.5V?

I can think of three possibilities - 1. Your bias circuit is bad. (Bad component or wired wrong.) 2. You are connected to the wrong input pin, or you are reading the wrong input pin. 3. Your Arduino has a fried analog input.

Hmmmmm. The Circuit is biased by a Voltage divider of 2 resistors same size (10k). The read value is divided by 4 (+128 to set digital silence to 2.5volts) The pin I assume is not fried, anyway i've tried with a different one with the same result. What is strange is that the same pin for input and output and the same circuit works fine if the read/White is done using microntroller timer prescaling commands and so on. Ok, maybe the sampling freq is not that much, i could double it or more... but why is the analogread always 1023 only when using C++/Arduino code? I mean i suppose it isn't with microntroller commands since the Pwm works fine. I'm confused. Thx

DVDdoug: The Arduino doesn't have a DAC... analogWrite() does NOT write analog! It writes a PWM analog which can approximate analog in some applications, such as dimming an LED. You're probably hearing the ~400Hz or ~900Hz PWM frequency.

Assuming you haven't added a DAC, that's your biggest issue right now. (The Arduino Due does have a DAC.)

The PWM is also only 8-bits, so you'd need to divide by 4.... But, that doesn't matter since PWM is not going to sound like "audio" anyway...

DVDdoug can you please elaborate a little bit the above?

Ok that PWM isn't "technically" DAC; it does no more than mapping a 8bit (0-255) value to set the correct percentage of the duty cicle switching the voltage LOW to HIGH... this in some application simulates an analog voltage.

What is not clear is yr last sentence "is not going to sound like "audio" anyway..."

There're many Arduino concepts playing audio over a PWM pin; generally they use timer/clocks prescaling to define a specific frequency on the I/O pins... but isn't this trick done to define a better quality of the audio, since by default the preset configuration of the microcontroller, will generate poor audio? Could it be done also with basic Arduino based code? (assuming the code or circuit is not wrong... :-) )

Thx

It's difficult, but not impossible, to achieve low quality to perhaps as good as telephone-quality audio on regular 8 bit Arduino boards. The default PWM frequencies are far too low, so this almost always involves timer code or a library like TimerOne to get higher PWM speeds. Usually another timer is used to run code are regular (low jitter) intervals to update the PWM, or read the ADC. It quickly gets complicated, where much is done with complex direct hardware access, to avoid any overhead, just to keep up with the audio sample rate. Because the CPU is so slow and the memory so limited, and the serials communication has so much overhead and so little buffering, troubleshooting such code is very hard.

Good quality audio gets much easier if you step up to the new 32 bit boards, and especially using codec chips meant for quality audio signals. Proper DMA (with a good library leveraging it), more memory, and a faster CPU makes a world of difference. At the risk of gratuitous self promotion, check out this video tutorial for a look at an advanced audio library I've been developing over the last couple years. If you check out that design tool, you'll see there's objects to route sound to a PWM and DAC pin, and from a built-in ADC pin. Obviously the sound quality isn't as high with the built-in pins, but it's still pretty decent.

So regarding your ultimate question "Could it be done also with basic Arduino based code", on 8 bit AVR the answer is pretty much no. It can be done on 8 bits with complicated advanced code that directly manipulates the hardware and avoids a lot of the simple but high-overhead stuff that make Arduino so simple and easy to use. On 32 bit ARM with a good library, as you can see in that video, the answer is very much yes, this can be done with very simple Arduino code.

Thanks Paul.

The quality is not so relevant right now, it's just a concept... but your answer about "Arduino based code" clarifies a lot. Instead of simplifying the life of the developer you'll have to manage several unwanted side effects to remove delays between the audio "caption" and it's manipulation, if I got it right.

So I guess I'll better do my best in understanding how microcontroller programming works and what is the trick behind the skipping of those "side effects"!

About the tutorial I've had a look, in the past days... it's amazing!!! :-) But I didn't want to go through buying additional hardware and studying more microcontrollers :-)... so I decided to try to struggle a little bit more!

Thx

Simone.

[quote author=Paul Stoffregen link=msg=2689500 date=1459427816]

At the risk of gratuitous self promotion, check out this video tutorial for a look at an advanced audio library I've been developing over the last couple years. If you check out that design tool, you'll see there's objects to route sound to a PWM and DAC pin, and from a built-in ADC pin. [/quote] Let's talk about teensy... Teensy does everything I need right? 'Read' audio from an external source, manipulate it thanks to the Arduino compatible IDE and is interfaced with a Pc by the USB, right? It has in-built ADC , PWM and DAC... Does it work stand-alone or it needs an Arduino 'underneath'? I may consider getting one! ;-)

Hi guys,

I'm having some serious problem with OpAmp circuits (audio)!!!!

Whatever chip I try (e.g. LM386, TDA2822) the amplification never rises above a gain of ~10... regardless the fact that I'm following the exact schematics included into the chip datasheet (e.g. the cap between pin 1 and 8 of LM386, that should in theory increase the gain to a *200, does nothing...).

The audio input oscillates in a range of ~ +-20mV... a gain of 10 in not enough to do AnalogRead with Arduino, as you may imagine.

Bad IC? Bad circuit-eer!? :-)

Any hint?

thx

Or...

Say the other way round!

Assuming I cannot reach gain greater than *10... can I just put several OpAmp in series in order to have *10*10*10... so on? If yes, how?

Any schematic/breadboard picture more than welcome.

thx.