Go Down

Topic: A bunch of questions about playback sampled sounds (Read 9169 times) previous topic - next topic

Lucario448

Hi there! As the subject says, I want to playback a sampled sound without too much stuff.

First of all, I just have a Nano board with ATmega328P (aka V 3.0), and I don't have yet my SD card module; so I wrote all the samples of an almost 2 seconds sound, into the MCU's flash memory (my sketch barely fits into that).
Those are unsigned 8 bit samples, monaural audio and should be played at 16 kHz. So, in theory, I need a delay of aprox. 62 microseconds per sample. I guess everything is fine up to here; but now these are my questions:

  • Since my board doesn't have a true DAC, can I use just a PWM pin to create sound (with a low-pass filter of course), or I should use the R-2R resistor ladder anyways?
  • If I could use PWM, the analogWrite function may cause timing issues? (delays more than 62 microseconds) If yes, please tell me an alternative way to very quickly change the duty cycle.
  • If the R-2R ladder is my only option, could be a bad idea to override the RX pin as a digital output, by using the port manipulation?
  • Would you mind suggest me a good way to amplify the final output? Because I think that the output itself isn't powerful enough to drive a speaker.


I will really appreciate your answers and I hope most of you can understand my questions.


PD: this might be obvious. Since I can't use a SD card yet, then I can't use the TMRpcm library and play WAV files. Yeah, I'm doing this "the hard way", I guess...

DVDdoug

Quote
1.Since my board doesn't have a true DAC, can I use just a PWM pin to create sound (with a low-pass filter of course), or I should use the R-2R resistor ladder anyways?
The resistor should be MUCH better than PWM.   It's still not a proper DAC because it's not "clocked" and you might get audio glitches when the DAC value changes.


Quote
2.If I could use PWM, the analogWrite function may cause timing issues? (delays more than 62 microseconds) If yes, please tell me an alternative way to very quickly change the duty cycle.
It shouldn't cause "timing issues", but you'll hear the PWM frequency along with your audio.   Here is some information about changing the PWM frequency.


Quote
3.If the R-2R ladder is my only option, could be a bad idea to override the RX pin as a digital output, by using the port manipulation?
I think it's a bad idea.  The Nano has 14 I/O pins and you only need 8 for the 8-bit resistor ladder.


Quote
4.Would you mind suggest me a good way to amplify the final output? Because I think that the output itself isn't powerful enough to drive a speaker.
You can use regular "powered" computer speakers, or you can plug-into your stereo system*, or get an amplifier.  Here is an example of a small power amp.




* Be very careful "playing around" if you have a high-power stereo system...   You might get a loud glitch that blows your speaker, or you can end-up generating a high-power ultrasonic signal that you can't hear, but that's powerful enough to blow a tweeter or an amp.

Grumpy_Mike

Quote
The resistor should be MUCH better than PWM.
No it is not. In theory it is the same but in practice it is much less due to the  tolerance on resistors.

Quote
Would you mind suggest me a good way to amplify the final output?
Any computer speaker or active speaker, these are very cheap.

thomai

Look at this great tutorial: Arduino Audio Output

I build a 8-bit, 8 voice synth on an Mega2560 and it sounds great (I used the Timer3-Library to get the sampling right at 20kHz).

First I tried it without the op-amps, but you need at least one buffer circuit (=2 op amps), otherwise the output signal is distorted if you play bass and high frequency at the same time (which sounds great sometimes  :) ).

The audio output is connected to a hifi-amplifier. Small in-ear headphones will work but even small speakers need much more power (at least you can hear the hi-frequency/noise tones if you listen carefully...).

Grumpy_Mike

Quote
Look at this great tutorial: Arduino Audio Output
Sorry that is an absolute crap tutorial, and if you think it is great you have a lot to learn.
It does carray a warning sign to say it is crap, this is the word
Instructables
In the URL.

It is crap because you can not achieve monotonicity on 8 bits with an R-2R ladder with the tolerance of resistor that you can actually buy at anything other than an extortionate price. That author has not a clue what he is talking about, he just makes it above the level of the thickos who post comments.

On this forum we are getting fed up acting as tech support for poor instructable articles.

AWOL

Quote
On this forum we are getting fed up acting as tech support for poor instructable articles.
Amen.

thomai

Having a bad day?

If you're quite new to electronics, imo this tutorial is the perfect way to start. It introduces all basic concepts and points in the right directions.

The kids who got their arduino at christmas may stop at the end of the page, but if you're really interested, this crappy little circuit is imo the best starting position for experimenting and digging deeper into each single concept.

Maybe in a commercial environment you'd use other circuits, but as a beginner you won't be able to understand them.

And what kind of audio quality do you expect when given a atmega328 with 8 bit resolution and 16kHz sample rate??? I expected almost nothing and it surprised me how far I can go with this simple setup.

Grumpy_Mike

Quote
Having a bad day?
Not until you showed up spouting rubbish no.

Quote
If you're quite new to electronics, imo this tutorial is the perfect way to start. It introduces all basic concepts and points in the right directions.
Just no on about every level you can think of. It does not point in any direction that is not a dead end.

Quote
Maybe in a commercial environment you'd use other circuits, but as a beginner you won't be able to understand them.
That is just rubbish.

The correct circuit is much easier to understand because:-
1) It is simpler.
2) The decisions you have to make in deciding what the circuit is are logical, rational and what is more you can carry the lessons learned onto other circuits.

The point is that knowledge is accumulative, you should be able to learn from one experience and apply that learning to others. To extend what you know.

It's like building a house, if you start off with crappy foundations then you might be able to build something a bit crappy as a first floor but anything greater will collapse.

Quote
And what kind of audio quality do you expect when given a atmega328 with 8 bit resolution and 16kHz sample rate?
A lot better than he achieved with that instructables.

Quote
I expected almost nothing and it surprised me how far I can go with this simple setup.
Well that shows you how much you know.

The setup could be simpler and the quality higher if he had done it right.
Take that buffer for example, can you explain why he uses that op amp when there are many that are better and cheaper. Can you also explain why he only uses one op amp in the package and so has to use two packages? Can you explain why he uses two batteries to power them?

What instructables are, are just some rank beginner who has cobbled something together with little or no understanding of what they have done. Then they say like a three year old "look at me". You do exactly what I have done and this will happen. That only works if they do "exactly" what he has done, which in most cases is not possible due to obscure components or inadequate design only working because all the tolerances went in his direction when he cobbled it up. Finally they are hyped up to appear to be ten times more than what they are.

In the mean time people think they have learned something, legitimately try and extend the project as proper learning would dictate, fall flat on their backsides, and come over here wanting help. We have to tell them all they thought they knew is wrong. They are sometimes reluctant to accept this and sometimes go away.

It is simpler to learn stuff that is right than to learn crap that is wrong.


The problem is that anyone can put one of these travesties up on line. There is no quality review or peer review at all. There are plenty of good stuff on line but you have to be able to sort it from the crap. Books on the other hand have some sort of minimum level of competency, but then you have to pay for that filter.

thomai

Ok, I understand why you're upset. And thank you very much for your time explaining everything.

What kind of technique would you suggest?

This one here?

Grumpy_Mike

#9
Feb 06, 2016, 02:16 pm Last Edit: Feb 06, 2016, 02:18 pm by Grumpy_Mike
Quote
What kind of technique would you suggest?
This one here?
That is one way of doing things, and is reasonable in its explanation. It will certainly deliver better results than the one you cited at first. The latest one you cited has a true 8 bit output and better restoration filter than most.

There are many ways to do this. The best way would be to use an proper A/D converter. Home made ones only work to about 5 to 6 bits and any more bits you add only add to the noise on the signal. Using a parallel A/D however is a poor decision because:-
1) The way the Arduino processor is made means you can't write 8 bits with one instruction, and writing in two or more instruction generates glitches in the signal unless there is a latch inside the A/D.
2) It simply takes up a lot of pins.
Using the 2R-R A/D ladder would be much better with only 5 bits and the pins used allocated intelligently so that the sample could be delivered in one direct port access instruction.

The best sort of A/D is one with a serial interface one, just a couple of pins and it is very quick send out data if you do it right. With the same interface you can have 8, 12 or more bits for the same number of interface pins.

There are two factors involved here, sample rate, how fast the samples are produced and quantisation rate, how many bits you have to specify a voltage. Both contribute to the noise and it is a bit of a balancing act as to what is best. The exact situation you are in contributes to that balancing act.

The limitations with an Arduino are mainly memory ones. You can generate signals like that last site you linked to, and you can generate any wave shape as well. For samples, like speech or music, without a source of external memory like an SD card you can only get about 3.5 seconds of audio. Which can be enough at times. I got a "yes" and "no" sample in only a quarter of the available memory.

pjrc

#10
Feb 07, 2016, 03:54 pm Last Edit: Feb 07, 2016, 03:54 pm by Paul Stoffregen
It is crap because you can not achieve monotonicity on 8 bits with an R-2R ladder with the tolerance of resistor that you can actually buy at anything other than an extortionate price.
And even if you do pay the extortionate price, half of those super-accurate resistors are series with the on-resistance of the MOSFET transistors inside the chip.  The N-channel (for low) and P-channel (for high) are similar, but not perfectly matched in resistance.  Their resistance also changes considerably, so even if you manage to carefully measure the transistor effects and correct your external resistors for the particular chip you have, it'll only match well at the exact temperature where you measured.

pjrc

The other huge problem with (simple) resistor ladders and also with PWM is the signal is directly derived from the power supply voltage.

Real DACs use a stable reference voltage, just like Arduino's ADC can use the internal reference, or an external one if you really care about an accurate reference.

When you use the power supply as a reference, any changes in the power supply voltage are reflected in the DAC analog output or ADC measured values.  For measuring pots, that's exactly what you want, since the power supply changes also change the voltage on the pot, so it using the power supply as a reference tends to cancel out those voltage changes.

But for a DAC, you'd almost never want to use the power supply as your reference voltage.  Any fluctuations or noise on the power supply go right into your analog signal.

Lucario448

Whoa guys take it easy!!! I know you wanna help me, and I appreciate that; but please, calm down. Now I'm kind of confused.

Ok guys, I actually don't pretend to take this too far away (until I get my SD card module), for now I'm fine with the "simple stuff".

Now I have a problem. I tried to reproduce the sound with a PWM pin and a RC low-pass filter. Also I used a LM386-based amp to catch up the final output to a pair of earbuds (unfortunately I didn't have any PC speaker on hand). Apart from a lot of white noise, I just heard a PWM signal changing its duty cycle very quickly (before and after the filter), instead of the actual audio. What's wrong? Is not possible by PWM, or the low-pass filter isn't doing its job?

I'm gonna to describe my low-pass filter and tell me if something is wrong.

It's a RC low-pass filter. It uses 200 ohms for resistence, and 0.1 uF (100 nF) for capacitance. Resistor goes in series with the output, and the capacitor is connected; one node between the resistor and the output; and the other to ground (as a RC low-pass filter is supposed to be). With that values, in theory, I should achieve the "cut-off point" at aprox. 8 kHz.
For resistence, I used two 100 ohm resistor in series. And for capacitance, a small orange ceramic capacitor with a "104" printed on it.

Please tell me if I made something wrong. Also I'm gonna attach the source code file in case of something else.


PD: my apologies if my texts are a bit difficult to understand, that's because english is not my native language. And thanks by the way...

Grumpy_Mike

#13
Feb 08, 2016, 06:43 am Last Edit: Feb 08, 2016, 06:45 am by Grumpy_Mike
I can't read a .ino file on an iPad why did you not just post it normally  using code tags.
Have you changed the default frequency of the PWM. It needs to go at least four times the sample rate.

That filter sounds wrong, the capacitor does not go on the output pin but the other end of the resistor to ground. Ceramic is a poor choice for audio work as it is not a very stable capacitance.

Lucario448

I can't read a .ino file on an iPad why did you not just post it normally  using code tags.
Oh, my bad. Code at the end of the replay.
Quote
Have you changed the default frequency of the PWM. It needs to go at least four times the sample rate.
How to do that? Is that "healthy" for the MCU? The change is permanent or just depends of the sketch itself? Will that affect the "delay" functions?
Quote
That filter sounds wrong, the capacitor does not go on the output pin but the other end of the resistor to ground. Ceramic is a poor choice for audio work as it is not a very stable capacitance.
Yeah. Another mistake of mine. When I said "output", I mean the output of the signal, not the actual output pin. And yes, one node after the resistors and the other to ground, as I said before. So, do you suggest me a electrolytic capacitor? A electrolytic one of 0.1 uF does actually exist?


And now here's the code (be aware that the samples' array were trimmed due to the character count limit of the post):
Code: [Select]
/*
 * This is the source code of a built-in-audio player.
 * At this way, it's attempting to reproduce a sound
 * with PWM.
 *
 * Made by Lucario448
 */

const unsigned char data[28167] PROGMEM =
/*
 * Never loaded into RAM, this very long array is read directly
 * from the flash memory.
 * I don't know if it could be a unsigned byte array, since
 * this is generated by program called bin2h.
 *
 * This array contains all the necessary samples to reproduce
 * the sound (displayed in hex form). Should be at a rate of 16 kHz.
 */
{
  0x7E, 0x7D, 0x7D, 0x7E, 0x7F
};
// The actual array were trimmed due to the character count limit of the post.
// The full array is within the file attached in the previous replay.


const int speaker = 9; // using pin 9 as the PWM output.
void setup() {
  pinMode(speaker, OUTPUT);
  // setting up the pin. I don't know if this step is redundant.

}

void loop() {
  // Currently not using a way to stop playing it unless by shutting-down the board.
  // So, this will keep looping the sound forever.
  for (short i; i < sizeof(data); i++) { // Scan throughout the array above
    analogWrite(speaker, pgm_read_byte(data[i]));
    // Assuming that this function takes a few CPU cycles to execute, maybe not true.
    delayMicroseconds(62);
    // If the comment above is true, then this delay should allow to playback the
    // samples at the desired 16 kHz.
  }
}

Go Up