Monday 2 October 2023

6502 : BEN2 : Sound

Theory

We already have a variety of uses for the 65C22 Versatile Interface Adapter outputs: LED, LCD, Shift Register/LED bar, interrupts/clock ticks.  We haven't previously provided any sound output and I was attracted to a brief article "Add sound to your 6502 for 10 cents and a speaker".
The article explains that all you have to do is connect a small 8 Ohm, 1 Watt speaker to VIA output pin PB7 in series with a 47uF capacitor and a 100 Ohm resistor.  You can then generate a square wave output from the VIA which translates to a sound of the corresponding frequency on the speaker.

Proof of Concept

 I don't have PB7 free at present so I will have to generate the wave form myself. I have recently attached a 74HC595 shift register (SR) to the VIA to provide extra outputs so I can easily use one of those outputs.

It occurred to me that my clock timer generates interrupts at 10ms intervals.  If I toggle an SR output in the interrupt service routine I generate a square wave output with period 20ms which will give me an output frequency of 1000/20  = 50 Hz.  This is the same frequency as mains hum so I should hear a low buzz from the speaker.

I connected up a little buzzer I had available (from my kit of 37, mainly useless, arduino peripherals) to a resistor, capacitor and SR output 0.  I added a couple of instructions to my interrupt service routine to toggle SR0 each time an interrupt is generated.  The test worked first time and I was able to hear a, rather quiet, low buzzing sound.  😀😀😀


The sound pitch is rather low for my liking, it is determined by the rate at which the timer generates interrupts.  If I decrease the period between interrupts, for example so that the period is 1ms, the pitch increases from 50Hz to 500Hz which is a lot better, it sounds approximately like middle C.  However an interrupt each millisecond only leaves 1000 cycles between interrupts for all interrupt processing, which may be limiting.

One positive aspect of using the ISR for sound generation is that programs can continue with other tasks whilst the sound continues.

For the next investigation I will setup sound as a general purpose subroutine which any program can call to leave a beep, buzz or ping.

Volume

The sound of the speaker is masked by other sounds in the room.  Although I dont want loud irritating sounds coming out of the buzzer I would like it to be somewhat more noticeable.  I tried two other speakers attached to the 6502, a 5cm 8 Ohm 1 Watt and a 1CM 8 Ohm, 0.5 watts.  The 5cm speaker was a bit louder and the 1cm speaker was quieter.

I also tried changing the 100 Ohm resistor value.  All three options were somewhat louder when there was no resistor in series.

I have a little PAM8403 5v, 3W+3W stereo amplifier, which I felt might resolve my issue.  It sometimes worked and made the sound louder and sometimes produced crackles.  I think the input voltage/current was too high causing it to mis-function.  I also tried to add a volume control to the amplifier input but it didn't seem to make a difference.  

It occured to me that if I need a small gain, of say 5, I should be able to make a simple transistor circuit to function as an amplifier, but I didn't feel like diverting myself to "analog" work.

It also occured to me that the shift register may not provide as much current output as the VIA so I changed my program to output to IVA pin PA1, which has a button attached.  The sound was a bit louder again so the VIA does provide a more power.

I believe I could get the amplifier working if I look into the hardware capabilities and voltage/currents in the circuit.  However, I decided, for the moment to stick with my 5cm speaker with no in-series resister and to utilise VIA output.  This combination provides an acceptable volume without making the system more complicated.

Sound Subroutine

 The sounds subroutine is simple.  An inner loop (lines 178-186 in the example below) is executed to toggle an LED bit.  A delay (mid_delay in the example below) is added into the loop to alter the sound frequency; a larger delay causes a lower frequency, no delay leads to the highest pitch.  The x register controls how many times the loop is executed, ie how long the sound is output for.


The sounds subroutine can be called with different values of A-reg for frequency and X-reg for duration.

At this stage I realised that higher notes sound quite musical so I may well be able to play simple tunes on the computer.

Tuning

It is common to denote musical notes in 8 octaves, each containing 8 notes.  Middle C is C4 and notes going upwards are C4, C#4, D4,D#4,E4,F4,F#4, G4, G#4, A4, A#4, B4, C5... This is slightly confusing.  Each octave has 12 notes, the 8 "white keys" and 5 sharp (#) "black keys" on the piano.
If music is played in the key of C major it doesn't need any sharps, other keys require sharps to make them sound right.
 The pitch (frequencies) of musical notes are related mathematically with the reference note being A4 (440Hz), If we can determine the parameters for one note (middle C, in my case) we can calculate appropriate values for other notes.

I found a convenient piano program webpage which allowed me to play notes.  I started by adjusting the A-reg value in my sounds subroutine until one sounded pretty much like middle C, I then calculated the frequencies and corresponding A-reg values for other notes.
Higher notes take less time to play so I also had to calculate durations for the X-reg values so that notes are all the same length.
This gave me a table of values for A-reg FREQUENCY and X-reg DURATION.  I added these a symbols in my program so that I can play notes easily.



Note Lengths

I can now play a sequence of notes and we have the beginnings of a tune.  The notes I chose were E4, D4, C4 which happen to be the first three notes of "three blind mice", and this has become my mission, to be able to play the nursery rhyme out of the computer speaker.

Notes have different lengths.  Having set up the durations shown above I judged that the shortest note that I can play is a semi-quaver which represents once round a loop.  Quavers, crotchets minims, ... require 2, 4, 8 iterations.

The notes sound a bit strange as there is no gap between them.  If you were pressing piano keys there would be a short lull between notes so I needed to add a gap.  I did this by making a delay based on 1/8th of each note, so a shorter note has a smaller gap after it.

I also tidied up my testing code a bit and renamed my subroutine from sounds to note as we are not playing a buzzer anymore we are playing music!

Three Blind Mice

We are now ready to play a tune.  I setup the notes as frequency, duration, length triplets in memory and provided a loop to load each triplet and play the corresponding note.  It actually sounds ok. 😀😀😀





End Note

This is excellent.  I started out wanting a simple buzzer and I have ended up with the ability to play nursery rhymes.  I have another type of output available.  It could be very useful during debugging as it is independent ot screen or LCD output.




No comments:

Post a Comment