Distorted Audio Can't Fix

Hi, I'm trying to make a circuit that uses the ATTiny85 to make a digital music box, that plays a song such as happy birthday. But currently, the loudspeaker plays the song very distortedly, such as static noises. Although you can hear the audio, it's very messy. I've tried implementing the recommended solutions like putting 100nF and 47uF capacitors but so far it hasn't worked at all. So, I'm not sure if I'm doing it right, or if there's another solution to this problem.

Here is the schematic diagram:

Here is what the actual project looks like (it still has my attempt of putting more capacitors to solve it):

And here is the code:

/* Digital Music Box v2

   David Johnson-Davies - www.technoblogy.com - 16th March 2016
   ATtiny85 @ 16 MHz (internal PLL; 4.3 V BOD)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license: 
   http://creativecommons.org/licenses/by/4.0/
*/

int Scale[] = { 
  680,  721,  764,  809,  857,  908,  962, 1020, 1080, 1144, 1212, 1284, 
 1361, 1442, 1528, 1618, 1715, 1817, 1925, 2039, 2160, 2289, 2425, 2569,
 2722, 2884, 3055, 3237, 3429, 3633, 3849, 4078 };
 
const int Channels = 4;
const int Tempo = 4;      // 4 = 4 beats per second
const int Decay = 9;      // Length of note decay; max 10

volatile unsigned int Acc[Channels];
volatile unsigned int Freq[Channels];
volatile unsigned int Amp[Channels];

// Play Happy Birthday

const uint32_t Tune[] PROGMEM = {
//_*_*__*_*_*__*_*__*_*_*__*_*__*_ff
//C D EF G A BC D EF G A BC D EF G
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00001001000000000001000000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000010000000,
0b00000000000000000000000000000000,

0b00100000000000000000000100000000,
0b00000000000000000000000000000000,
0b00000101000000000000000000000000, 
0b00000000000000000000000000000000,
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00000101000000000001000000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000000100000,
0b00000000000000000000000000000000,

0b10000000000000000000000010000000,
0b00000000000000000000000000000000,
0b00001001000000000000000000000000,
0b00000000000000000000000000000000,
0b00000000000000000001000000000000,
0b00000000000000000001000000000000,

0b10000000000000000000000000000001,
0b00000000000000000000000000000000,
0b00100000000000000000000000001000,
0b00000000000000000000000000000000,
0b00001000000000000000000010000000,
0b00000000000000000000000000000000,

0b00000100000000000000000100000000,
0b00000000000000000000000000000000,
0b00000000000000000000010000000000,
0b00000000000000000000000000000000,
0b00000000000000000000000000000100,
0b00000000000000000000000000000100,

0b00000001000000000000000000001000,
0b00000000000000000000000000000000,
0b00000000000000000000000010000000,
0b00000000000000000000000000000000,
0b00000101000000000000000000100000,
0b00000000000000000000000000000000,

0b10001001000000000000000010000000,
0xFF}; // End of tune

//Globals persist throughout tune
int TunePtr = 0, Chan = 0;

// Watchdog interrupt plays notes
ISR(WDT_vect) {
  sei();   // Allow interrupts
  WDTCR |= 1<<WDIE;
  unsigned long Chord = pgm_read_dword(&Tune[TunePtr]);
  if (Chord == 0xFF) return;
  TunePtr++;
  // Read the bits in Chord
  for (int Note = 0; Note < 32; Note++) {
    if ((Chord & 0x80000000) != 0) {
      Freq[Chan] = Scale[Note];
      Amp[Chan] = 1<<(Decay+5);
      Chan = (Chan + 1) % Channels;
    }
    Chord = Chord<<1;
  }
}

// Generate square waves on 4 channels
ISR(TIMER0_COMPA_vect) {
  signed char Temp, Mask, Env, Note, Sum=0;
  for (int c = 0; c < Channels; c++) {
    Acc[c] = Acc[c] + Freq[c];  
    Amp[c] = Amp[c] - (Amp[c] != 0);
    Temp = Acc[c] >> 8;
    Mask = Temp >> 7;
    Env = Amp[c] >> Decay;
    Note = (Env ^ Mask) + (Mask & 1);
    Sum = Sum + Note;
  }
  OCR1B = Sum + 128;
}

void setup() {
    // Enable 64 MHz PLL and use as source for Timer1
    PLLCSR = 1<<PCKE | 1<<PLLE;     
  
    // Set up Timer/Counter1 for PWM output
    TIMSK = 0;                     // Timer interrupts OFF
    TCCR1 = 1<<CS10;               // 1:1 prescale
    GTCCR = 1<<PWM1B | 2<<COM1B0;  // PWM B, clear on match
  
    OCR1B = 128;
    DDRB = 1<<DDB4;                // Enable PWM output on pin 4
  
    // Set up Timer/Counter0 for 20kHz interrupt to output samples.
    TCCR0A = 3<<WGM00;             // Fast PWM
    TCCR0B = 1<<WGM02 | 2<<CS00;   // 1/8 prescale
    OCR0A = 99;                    // Divide by 100
    TIMSK = 1<<OCIE0A;             // Enable compare match, disable overflow
   
    // Set up Watchdog timer for 4 Hz interrupt for note output.
    WDTCR = 1<<WDIE | Tempo<<WDP0; // 4 Hz interrupt
}

void loop() {
   
}

You can not drive a speaker directly from a pin.

I suppose you could with a piezo speaker (they typically draw less than 10mA). They can get pretty loud at their resonant frequency.

I would suggest getting a decent power supply, the PP9 battery will not support any load which the speaker will happily provide. Then you get to check if the distortion if form the power supply or your computer/software.

I've tried with a piezo speaker, it seems the audio is still distorted even with the buzzer.

Should I get a stronger battery if that's the case? I have a 5V regulator in order to avoid it overloading the loudspeaker, or causing problems with the ATTiny85, so I'm not sure if that would work. What kind of battery would you suggest?

I have a capacitor to avoid it driving directly into the speaker itself. Is there a better way to approach this? I'm not sure what exactly you mean, apologies.

Yes.

The arduino can not provide enough power to give a decent sound from your 8 ohm speaker.
With cap coupling you are getting a 2.5V signal. Connect the speaker to that and it wants 300mA.
The pin cant deliver that.

To get decent sound you need to either use a higher impedance speaker - but it will be QUIET.
(shh)
or better, use an aplifier like this

(NOT a 386 based amplifier if you want to run from a 5V supply)

There are two different types of piezo speaker. One type beeps when you apply DC voltage. You don't want that type (it'll work if all you want is a beep at a single frequency, like a smoke alarm).

The other type is more like a regular speaker (it reproduces the sound signal that you feed it). That's the type you want.

Thank you for the help, I'll make sure to check it out! In terms of the higher impedance speaker, what specific resistance would you suggest? And if I were to use the Audio Amplifier, how could I connect it so it only uses one output pin from the ATTiny85? I noticed that the example they provided uses 2 output pins from the Arduino.

I'm not exactly sure for the ATTiny85 but a piezo transducer will be OK. (The regular Arduino is rated for 40mA maximum and that works out to a minimum resistance/impedance of 125 Ohms). Most speakers are 4 or 8-Ohms so a regular speaker won't work without an amplifier. (You can put a 120-Ohm resistor in series with the speaker but it will be very quiet.)

A piezo transducer's impedance is (approximately) capacitive reactance. It's probably around 1000-Ohms at 1kHz so it's OK to drive directly.

Piezos are tricky to buy because like christop says, there are two different kinds and they are often mis-named but the seller. You want a "speaker" or "transducer", NOT a "buzzer" or "beeper".

OIne way to be 100% sure is to buy a piezo tweeter.

If there is a DC voltage spec or mA spec, it's "buzzer" with a built-in sound generating circuit and it's the wrong kind.

Piezo speakers are "tweeters" and they are not good at mid or low frequencies. No small speaker is good for low frequencies (bass). Musical greeting cards have piezo speakers.

If you have powered computer speakers, try those. That will give you an idea of the best sound quality you can get from a simple square wave.

You need one signal connection and a ground between the amplifier and the Arduino. The amplifier also needs power.

A lot of amplifiers are stereo so there are 3 connections (left, right, and ground like a headphone plug). If you have a stereo amplifier it's OK to connect the left & right inputs together so you get sound out of both speakers. (It's NOT OK to connect outputs together!))

...It would be "good practice" to put a high-pass filter between the Arduino and the amplifier because the Arduino puts-out pulsed-DC. Regular audio is AC (going positive & negative) and since DC is zero Hz, a high-pass filter blocks the DC allowing the signal to go positive and negative. Most amplifiers already have a DC-blocking capacitor on the input but it would be better to be sure.

Thank you so much for all the help! I'll let you know if I run into any problems again.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.