From Digital Bits to Sound Waves: Arduino Nano Synth Project

asd

Description

Even though I went to an elementary school focused on music, I was in the only non-musical class. It wasn’t so bad… I didn’t have much interest in music anyway.

I regret it a little, especially after I saw Ben Eater’s new video “Computer Noises”, where he used his breadboard 6502 computer to generate a PWM signal to play different notes. I was vaguely familiar with sound generated by code, but I had never done it myself or researched the topic. Around the same time, I received a few Arduino Nano clones from Aliexpress and decided I wanted to try it!

As expected, I wanted to do more than just call the ‘tone()’ function. I fell into a rabbit hole of learning what digital and analog sound are, the math behind it, and how to read notes. In the end, I’m happy with the result and my new knowledge about sound and music.

Consulting AI

Sometimes consulting AI to learn about a topic of interest can be annoying because it yaps too much. Google Gemini solved this with a feature called “Guided Learning”, where it gives small, incremental explanations and then asks you questions. This was my starting point to learn about music. What pitch, notes, octaves, and rests are. From there, I was ready to watch a video series on YT, how to read sheet music and put all of it together.

I was convinced that I could build this the next day… Oh boy, I was so wrong. Understanding how to create sound from digital input took me so long, this wasn’t clicking at all. After the high of understanding something new, I was brought back down by not understanding anything again… A classic sine wave of learning.

Digital to Analog

Another stop on my journey was discovering that I needed to convert 1s and 0s into a voltage range (DAC) to make the speaker play something. From building an 8-bit computer on a breadboard, I had tons of resistors, transistors, capacitors, and ICs left over. The most common way to build a DAC is to use an R-2R ladder. All I needed were resistors, and I had a big bag of 1kΩ resistors, so I used them. By changing the input values, in our case, 1s and 0s we get different voltage levels on the output. How precise this output is depends on how large our ladder is. In my opinion, the sweet spot for this project is 8-bit.

12-bit or more is overkill for learning, and 4-bit just doesn’t feel right. I had two options to connect this ladder to the Arduino Nano. Use 8 pins and control each one, or dig into my IC collection and use a shift register. Option 2 won. I used a 74HC595 8-bit shift register.

I wrote simple code to shift data into the 595 connected to the R-2R. GPT recommended that I loop over pre-calculated values of a sine wave, so I did that. The result on the oscilloscope looked fine to my amateur eyes. I felt like I was unstoppable once again and this time, I could finish it in half a day.

Reality check again

Let’s connect a small speaker and hear how this sine wave sounds. Nothing happened. Did I break something? Maybe the speaker doesn’t work… I leaned back in my chair, disappointed with myself again. After thinking it over with AI, yeah, I needed something called an amplifier. This was the biggest stop on my journey. I’ll confess. Understanding the concept of what an amplifier is and how it works took me a few days.

I was too deep into this! I wasn’t going to buy amplifier ICs from Aliexpress. I will build one myself! Luckily for me (and for this project), I was able to build an amp using an transistor, a few resistors, and some capacitors. A Class A amplifier circuit uses only one transistor, so that’s the one for me.

A few videos, articles, calculations, and days later, I think, I got somewhere! The speaker is working, something is playing… it’s the sound of a sine wave, I hope.

But by now, you know the drill… after putting it all together and trying to play “music” based on notes, reality came knocking on my door again.

The timing of the notes was off, the notes sounded off, the signal on the oscilloscope looked off, back to the AI chat drawing board.

In the end, the distortion was too much and the volume was too low or after tinkering with capacitors, the volume was too high. So I tried using a Class AB amplifier with an NPN and a PNP transistor. Building it was much easier after all the trial and error with the single transistor setup, and right from the start the volume was perfect with minimal distortion. To make it even better, I could add two diodes, but I don’t have any, so I’m happy with the result.

asd

Analog to Digital back to Analog

Ok, let’s try to learn how we get these 1s and 0s from an analog signal in the first place… Alright, so something called sample rate and bit depth exists… I know that! I’ve seen it when setting my audio settings on my PC! Ok, it’s not that hard. Sample rate is just the number of samples taken over a period of time, and bit depth is the resolution, how precise the values are for each sample.

A few rewrites, some tuning, and more reading later, I finally had something usable. I am happy with this… Sounds fine, volume level is good to and minimal distortion too.

Chiisana Boukensha - Konosuba! ED

0:00 0:00

Code

Arduino / C++
#include <Arduino.h>
#include <SPI.h>
#include <avr/pgmspace.h>

#define SHIFT_RCLK 3    // Latch Pin (PD3)
// SPI MOSI = Pin 11
// SPI SCK  = Pin 13

// Calculate Timer Value: (16,000,000 / SampleRate) - 1
// 16000000 / 22050 = ~725.6 -> 725

// #define SAMPLE_RATE 22050.0 
// #define TIMER_COMPARE 725 
#define SAMPLE_RATE 44000.0 
#define TIMER_COMPARE 363

// --- MUSIC DEFINITIONS ---
#define WHOLE 4.0
#define HALF 2.0
#define QUARTER 1.0
#define EIGHTH 0.5
#define SIXTEENTH 0.25
#define TRIPLET (1.0/3.0)

// --- GLOBAL VARIABLES (Volatile is required for Interrupts) ---
volatile unsigned int phaseAccumulator = 0; // Current position in sine wave
volatile unsigned int phaseIncrement = 0;   // Speed (Pitch) - "0" means silence
volatile uint8_t idx = 0;

// https://ppelikan.github.io/drlut/
const uint8_t sine256[256] PROGMEM = {
127,130,133,136,139,143,146,149,152,155,158,161,164,
167,170,173,176,178,181,184,187,190,192,195,198,200,
203,205,208,210,212,215,217,219,221,223,225,227,229,
231,233,234,236,238,239,240,242,243,244,245,247,248,
249,249,250,251,252,252,253,253,253,254,254,254,254,
254,254,254,253,253,253,252,252,251,250,249,249,248,
247,245,244,243,242,240,239,238,236,234,233,231,229,
227,225,223,221,219,217,215,212,210,208,205,203,200,
198,195,192,190,187,184,181,178,176,173,170,167,164,
161,158,155,152,149,146,143,139,136,133,130,127,124,
121,118,115,111,108,105,102, 99, 96, 93, 90, 87, 84,
 81, 78, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49,
 46, 44, 42, 39, 37, 35, 33, 31, 29, 27, 25, 23, 21,
 20, 18, 16, 15, 14, 12, 11, 10,  9,  7,  6,  5,  5,
  4,  3,  2,  2,  1,  1,  1,  0,  0,  0,  0,  0,  0,
  0,  1,  1,  1,  2,  2,  3,  4,  5,  5,  6,  7,  9,
 10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29,
 31, 33, 35, 37, 39, 42, 44, 46, 49, 51, 54, 56, 59,
 62, 64, 67, 70, 73, 76, 78, 81, 84, 87, 90, 93, 96,
 99,102,105,108,111,115,118,121,124 
};

enum Notes {
  NOTE_REST,
  NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, 
  NOTE_FS1, NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1,
  NOTE_C2, NOTE_CS2, NOTE_D2, NOTE_DS2, NOTE_E2, NOTE_F2, 
  NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2, NOTE_B2,
  NOTE_C3, NOTE_CS3, NOTE_D3, NOTE_DS3, NOTE_E3, NOTE_F3, 
  NOTE_FS3, NOTE_G3, NOTE_GS3, NOTE_A3, NOTE_AS3, NOTE_B3,
  NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, 
  NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
  NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, 
  NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
  NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, 
  NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
  NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, 
  NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7,
  NOTE_C8, NOTE_CS8, NOTE_D8, NOTE_DS8, NOTE_E8, NOTE_F8, 
  NOTE_FS8, NOTE_G8, NOTE_GS8, NOTE_A8, NOTE_AS8, NOTE_B8
};

const int noteFreq[] PROGMEM = {
  0,    // REST
  33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62,               // Octave 1
  65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123,           // Octave 2
  131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,   // Octave 3
  262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,   // Octave 4
  523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,   // Octave 5
  1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, // Octave 6
  2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, // Octave 7
  4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902  // Octave 8
};

struct NoteEvent {
  uint8_t note;
  float beats;
};

// --- KONOSUBA THEME --- BPM = 79
const NoteEvent song[] PROGMEM = {
  {NOTE_C5, QUARTER}, {NOTE_C5, EIGHTH}, {NOTE_D5, EIGHTH}, 
  {NOTE_E5, QUARTER}, {NOTE_E5, EIGHTH}, {NOTE_G5, EIGHTH},
  
  {NOTE_A5, SIXTEENTH}, {NOTE_C6, SIXTEENTH}, {NOTE_A5, SIXTEENTH}, {NOTE_G5, SIXTEENTH}, 
  {NOTE_E5, QUARTER}, {NOTE_E5, EIGHTH}, {NOTE_D5, EIGHTH},
  
  {NOTE_C5, QUARTER}, {NOTE_A5, QUARTER}, 
  {NOTE_G5, SIXTEENTH}, {NOTE_E5, SIXTEENTH}, {NOTE_D5, SIXTEENTH}, {NOTE_G4, SIXTEENTH},
  
  {NOTE_C5, QUARTER}, {NOTE_C5, EIGHTH}, {NOTE_D5, EIGHTH}, 
  {NOTE_C5, HALF}
};

// --- KOROBEINIKI (Tetris Theme) --- BPM = 160
// const NoteEvent song[] PROGMEM = {
//   // -- SECTION A --
//   {NOTE_E5, QUARTER}, {NOTE_B4, EIGHTH}, {NOTE_C5, EIGHTH}, {NOTE_D5, QUARTER}, 
//   {NOTE_C5, EIGHTH}, {NOTE_B4, EIGHTH},
//   {NOTE_A4, QUARTER}, {NOTE_A4, EIGHTH}, {NOTE_C5, EIGHTH}, {NOTE_E5, QUARTER}, 
//   {NOTE_D5, EIGHTH}, {NOTE_C5, EIGHTH},
//   {NOTE_B4, QUARTER + EIGHTH}, {NOTE_C5, EIGHTH}, {NOTE_D5, QUARTER}, {NOTE_E5, QUARTER},
//   {NOTE_C5, QUARTER}, {NOTE_A4, QUARTER}, {NOTE_A4, QUARTER}, {NOTE_REST, QUARTER},
  
//   // -- SECTION B (High Part) --
//   {NOTE_D5, QUARTER + EIGHTH}, {NOTE_FS5, EIGHTH}, {NOTE_A5, QUARTER}, 
//   {NOTE_G5, EIGHTH}, {NOTE_FS5, EIGHTH},
//   {NOTE_E5, QUARTER + EIGHTH}, {NOTE_C5, EIGHTH}, {NOTE_E5, QUARTER}, 
//   {NOTE_D5, EIGHTH}, {NOTE_C5, EIGHTH},
//   {NOTE_B4, QUARTER}, {NOTE_B4, EIGHTH}, {NOTE_C5, EIGHTH}, 
//   {NOTE_D5, QUARTER}, {NOTE_E5, QUARTER},
//   {NOTE_C5, QUARTER}, {NOTE_A4, QUARTER}, {NOTE_A4, QUARTER}, {NOTE_REST, QUARTER}
// };

// --- IMPERIAL MARCH (Star Wars) --- BPM = 108
// const NoteEvent song[] PROGMEM = {
//   // Main Riff
//   {NOTE_G4, QUARTER}, {NOTE_G4, QUARTER}, {NOTE_G4, QUARTER},
//   {NOTE_DS4, EIGHTH + SIXTEENTH}, {NOTE_AS4, SIXTEENTH}, {NOTE_G4, QUARTER},
//   {NOTE_DS4, EIGHTH + SIXTEENTH}, {NOTE_AS4, SIXTEENTH}, {NOTE_G4, HALF},
  
//   // Second Phrase (Higher)
//   {NOTE_D5, QUARTER}, {NOTE_D5, QUARTER}, {NOTE_D5, QUARTER},
//   {NOTE_DS5, EIGHTH + SIXTEENTH}, {NOTE_AS4, SIXTEENTH}, {NOTE_FS4, QUARTER},
//   {NOTE_DS4, EIGHTH + SIXTEENTH}, {NOTE_AS4, SIXTEENTH}, {NOTE_G4, HALF}
// };


// --- FUR ELISE (Beethoven) --- BPM = 65
// const NoteEvent song[] PROGMEM = {
//   // -- Main Theme --
//   {NOTE_E5, SIXTEENTH}, {NOTE_DS5, SIXTEENTH}, 
//   {NOTE_E5, SIXTEENTH}, {NOTE_DS5, SIXTEENTH}, 
//   {NOTE_E5, SIXTEENTH}, {NOTE_B4, SIXTEENTH}, 
//   {NOTE_D5, SIXTEENTH}, {NOTE_C5, SIXTEENTH}, 
//   {NOTE_A4, QUARTER},   {NOTE_REST, SIXTEENTH},
  
//   // -- Arpeggio 1 (Low to High) --
//   {NOTE_C4, SIXTEENTH}, {NOTE_E4, SIXTEENTH}, {NOTE_A4, SIXTEENTH},
//   {NOTE_B4, QUARTER},   {NOTE_REST, SIXTEENTH},

//   // -- Arpeggio 2 --
//   {NOTE_E4, SIXTEENTH}, {NOTE_GS4, SIXTEENTH}, {NOTE_B4, SIXTEENTH},
//   {NOTE_C5, QUARTER},   {NOTE_REST, SIXTEENTH},
  
//   // -- Arpeggio 3 --
//   {NOTE_E4, SIXTEENTH}, {NOTE_E5, SIXTEENTH}, {NOTE_DS5, SIXTEENTH},
  
//   // -- Repeat Theme --
//   {NOTE_E5, SIXTEENTH}, {NOTE_DS5, SIXTEENTH}, 
//   {NOTE_E5, SIXTEENTH}, {NOTE_B4, SIXTEENTH}, 
//   {NOTE_D5, SIXTEENTH}, {NOTE_C5, SIXTEENTH}, 
//   {NOTE_A4, QUARTER},   {NOTE_REST, SIXTEENTH},

//   // -- Ending 1 --
//   {NOTE_C4, SIXTEENTH}, {NOTE_E4, SIXTEENTH}, {NOTE_A4, SIXTEENTH},
//   {NOTE_B4, QUARTER},   {NOTE_REST, SIXTEENTH},

//   // -- Ending 2 --
//   {NOTE_E4, SIXTEENTH}, {NOTE_C5, SIXTEENTH}, {NOTE_B4, SIXTEENTH},
//   {NOTE_A4, QUARTER} // Final Note
// };


// --- FUKASHIGI NO CARTE (Bunny Girl Senpai ED) ---  BPM = 90
// const NoteEvent song[] PROGMEM = {
//   {NOTE_REST, HALF + QUARTER}, 
//   {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET},
  
//   {NOTE_A5, QUARTER}, {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET}, 
//   {NOTE_A5, QUARTER}, {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET},
  
//   {NOTE_E5, QUARTER}, {NOTE_F5, QUARTER}, {NOTE_REST, EIGHTH}, {NOTE_C5, EIGHTH}, 
//   {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET},
  
//   {NOTE_A5, QUARTER}, {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET}, 
//   {NOTE_C6, QUARTER}, {NOTE_AS5, QUARTER},
  
//   {NOTE_A5, TRIPLET}, {NOTE_REST, TRIPLET}, {NOTE_E6, TRIPLET},
//   {NOTE_E6, TRIPLET}, {NOTE_REST, TRIPLET}, {NOTE_F6, TRIPLET}, 
//   {NOTE_C6, TRIPLET}, {NOTE_D6, TRIPLET}, {NOTE_C6, TRIPLET}, 
  
//   {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET},
  
//   {NOTE_A5, QUARTER}, {NOTE_C6, QUARTER}, {NOTE_A5, QUARTER}, {NOTE_G5, QUARTER},
//   {NOTE_E5, QUARTER}, {NOTE_F5, QUARTER}, {NOTE_A5, QUARTER}, 
  
//   {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET},
//   {NOTE_A5, QUARTER}, {NOTE_D5, TRIPLET}, {NOTE_A5, TRIPLET}, {NOTE_G5, TRIPLET}, 
//   {NOTE_F5, QUARTER}, {NOTE_CS5, QUARTER}
// };

// --- YORUSHIKA (FRIEREN) --- BPM = 204
// const NoteEvent song[] PROGMEM = {
//   // Bass Intro / Riff
//   {NOTE_B2, QUARTER}, {NOTE_F3, QUARTER}, {NOTE_A3, EIGHTH}, {NOTE_A3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
//   {NOTE_F2, QUARTER}, {NOTE_E3, QUARTER}, {NOTE_E3, EIGHTH}, {NOTE_E3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
//   {NOTE_G2, QUARTER}, {NOTE_F3, QUARTER}, {NOTE_A3, EIGHTH}, {NOTE_A3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
//   {NOTE_D2, QUARTER}, {NOTE_F3, QUARTER}, {NOTE_A3, EIGHTH}, {NOTE_A3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
  
//   {NOTE_B2, QUARTER}, {NOTE_A3, QUARTER}, {NOTE_C3, EIGHTH}, {NOTE_C3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
//   {NOTE_F2, QUARTER}, {NOTE_E3, QUARTER}, {NOTE_E3, EIGHTH}, {NOTE_E3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
//   {NOTE_G2, QUARTER}, {NOTE_F3, QUARTER}, {NOTE_A3, EIGHTH}, {NOTE_A3, EIGHTH}, 
//   {NOTE_REST, QUARTER},
  
//   // Melody Start
//   {NOTE_D2, QUARTER}, {NOTE_F3, QUARTER}, {NOTE_D5, QUARTER}, {NOTE_E5, QUARTER},
//   {NOTE_D5, HALF},    {NOTE_E5, QUARTER}, {NOTE_D5, QUARTER},
//   {NOTE_C5, HALF},    {NOTE_F5, QUARTER}, {NOTE_E5, QUARTER},
//   {NOTE_C5, QUARTER}, {NOTE_D5, HALF},    {NOTE_B4, QUARTER},
  
//   {NOTE_B4, QUARTER}, {NOTE_D5, QUARTER}, {NOTE_D5, QUARTER}, {NOTE_E5, QUARTER},
//   {NOTE_F5, HALF},    {NOTE_E5, QUARTER}, {NOTE_D5, QUARTER}
// };


// --- SETUP TIMER 1 ---
void setupTimer1() {
  cli();
  TCCR1A = 0; TCCR1B = 0; TCNT1 = 0;
  OCR1A = TIMER_COMPARE;
  TCCR1B |= (1 << WGM12) | (1 << CS10);
  TIMSK1 |= (1 << OCIE1A);
  sei();
}

void setup() {
  pinMode(SHIFT_RCLK, OUTPUT);
  digitalWrite(SHIFT_RCLK, LOW);

  SPI.begin();
  SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));

  setupTimer1();
}

//--- INTERRUPT SINE WAVE ---
// ISR(TIMER1_COMPA_vect) {
//   if (idx == 256) idx = 0;
//   uint8_t v = pgm_read_byte(&sine256[idx++]);
//   SPI.transfer(v);
//   PORTD |= (1 << 3);
//   PORTD &= ~(1 << 3);
// }

// --- INTERRUPT ---
ISR(TIMER1_COMPA_vect) {
  phaseAccumulator += phaseIncrement;
  
  if (phaseIncrement == 0) {
    // Silence
    SPI.transfer(0); 
  } else {
    uint8_t index = phaseAccumulator >> 8;
    uint8_t val = pgm_read_byte(&sine256[index]);
    SPI.transfer(val);
  }
  
  PORTD |= (1 << 3);  // Latch High
  PORTD &= ~(1 << 3); // Latch Low
}

// --- PLAYBACK FUNCTION ---
void playNote(uint8_t noteEnum, unsigned int durationMs) {
  if (noteEnum == NOTE_REST) {
    phaseIncrement = 0;
  } else {
    int freq = pgm_read_word(&noteFreq[noteEnum]);
    // "unsigned long" (UL) to prevent overflow during the multiplication
    phaseIncrement = (unsigned int)((freq * 65536UL) / SAMPLE_RATE);
  }

  delay(durationMs);
}

void loop() {
  unsigned int BPM = 79;
  unsigned int msPerBeat = 60000 / BPM;
  int len = sizeof(song) / sizeof(song[0]);

  for (int i = 0; i < len; i++) {
    uint8_t n = pgm_read_byte(&song[i].note);
    float b = pgm_read_float(&song[i].beats);
    unsigned int duration = b * msPerBeat;

    playNote(n, duration);
    
    // Tiny gap
    phaseIncrement = 0;
    delay(10); 
  }
  
  delay(2000);
}

Sources

Michael Parchaiski

Basic Music Theory Concepts

https://youtu.be/AmC_qmSODEk

Michael Parchaiski

Everything You Need to Know About Reading Music

https://youtu.be/JGGn5bEWCL4

The Mento Zone

How to Count Basic Rhythms

https://youtu.be/YfT0irsgB3Q

Musescore

element14 presents

Making a 12 bit DAC Using an Arduino

https://youtu.be/IDrWtgTb3D4

bitluni

DAC using R-2R resistor ladder

https://youtu.be/_tABS7AX8D8

Engineering Prof.

8-bit DAC Digital to Analog Converter Design

https://youtu.be/Y1H8HrrD4mQ

The Headphone Show

What is a DAC and why do you need one?

https://youtu.be/HOvubnenXyo

The Headphone Show

What does an amplifier actually do?

https://youtu.be/--LEDxMs4yk

The Headphone Show

Audio amp classes as fast as possible!

https://youtu.be/d8ug0NbaIds

GreatScott

Class A Audio Amp Tutorial

https://youtu.be/7Pv-j_R3YP4

AllAmericanFiveRadio

Transistor Push Pull Amplifier

https://youtu.be/e_SE4KQjYR8