Following on from my initial experiments in Arduino and AY-3-8910 this post looks at the sound generation capabilities in a little more detail and adds some basic MIDI control.
Warning! I strongly recommend using old or second hand equipment for your experiments. I am not responsible for any damage to expensive instruments!
These are the key tutorials for the main concepts used in this project:
If you are new to Arduino, see the Getting Started pages.
Parts list
- Arduino Uno.
- AY-3-8910 chip.
- Either GadgetReboot's PCB or patch using solderless breadboard or prototyping boards.
- 5V compatible MIDI interface.
- Jumper wires.
AY-3-8910 Sound Generation
The most basic means of sound generation is to use the three tone generators to generate square waves at a frequency set using the on-chip registers (note in the following data from the datasheet, the R numbers are in octal - so there are 16 registers in total):
I don't plan to get into the ins-and-outs of how to interface to the chip, instead I'll link off to some excellent discussions here:
The frequency registers have a 12-bit resolution, spread over two 8-bit registers as shown below.
The datasheet tells us how to calculate the value to write to the register for a specific frequency:
- Reg Value = System Clock / (16 * frequency)
For a 1MHz clock, the register value is thus 62500 / frequency, so the higher the frequency, the lower the register value. This means that concert A at 440Hz requires the value 62500 / 440 = 142, so:
- R1 = 142 >> 8;
- R0 = 142 & 0xFF;
For a 1MHz clock, the range of frequencies goes from 1MHz / 16 to 1 MHz / (16 * 4095) or ~62.5kHz to 15 Hz
The AY3891x library has a set of definitions that already defines the frequencies for each MIDI note from C0 to B8.
The volume for the note is set in another register:
When Mode=0, the amplitude is set by the 4-bit fixed level. When Mode=1, the amplitude is controlled by the built-in envelope generator.
The envelope generator is a "global" setting for all channels, so for finer control, people often wrote their own envelope generator, manipulating the volume levels directly.
The datasheet describes the four parameters required to define an envelope:
Envelopes have a cycle time, which is set by R13 and R14 in a similar way to the frequency. This time the formula is:
- Reg value = System Clock / (256 * env frequency)
Once again this is split over two registers, but this time supports a full 16-bit value.
There are graphical representations of what the combinations of the envelope bits mean, but I must confess I'm not entirely sure I understand them all and some don't seem to sound, at least at the frequency I've chosen.
The Circuit
I'm reusing the PCB from GadgetReboot from Part 1, but this time I've added the button in (it is connected to A5 and GND) and added headers to the UART connection.
Unfortunately the UART only has GND, TX, RX - to use it with one of my Arduino MIDI Interfaces also requires a 5V connection, so I've taken that from the SD card header.
The Code
The note-playing code comes from the "AY3891x_EX3_Simple_Tone" example, including the ATmega328 specific code for the 1MHz clock. There is a table of note frequencies already provided for notes C0 through to B8, so it is just a case of mapping these onto MIDI notes C0 (12) through B8 (119).
One thing I wanted was to support simple polyphony using all three channels. But that means deciding what to do when a fourth note comes in - i.e. to ignore it or to replace one of the existing playing notes. I've left options for both.
I also wanted to make use of the channel volume too, so it is relatively trivial to map the MIDI 0..127 note velocity values onto the 0..15 levels for the sound generator. This is using the "fixed" level mode mentioned earlier.
But I also wanted the option to play with the envelopes, so I've wired in the button and have an option to use that to change between the different envelopes. As I'm not attempting anything particularly complex right now, I just gone with a fixed 10Hz frequency for the envelope generator's cycle.
It won't win any prizes for synthesis, but it does work.
Find it on GitHub here.
Closing Thoughts
Fundamentally, without the envelope generation this is the same as a three-channel Arduino tone() function, but at the time that was pretty ground-breaking as it allowed a system to keep playing a tone without having to keep driving it from the CPU.
Add in the noise channel, amplitude control and envelopes and you can start to see why this is also a step up musically too.
But when you get to making custom, per-channel envelopes, or even manipulating the 4-bit level control as a simple DAC or PCM generator, then you can start to see how some of the outstanding chiptunes of the time could be generated.
But even in this simple form there is still a fair bit more that could be done. Some examples might be:
- Adding a potentiometer to control the envelope frequency. The when used with the triangle envelope this will act as a modulation control.
- Add MIDI control values for the volume levels, modulation and choice of envelope.
- Define some specific parameters to create "instruments" which can be selecting using MIDI program change messages.
- Get the noise generator into the mix and define some percussion "instruments" too.
But what I really want to do is start taking a look at some of the sound drivers that were written that allow some of the chip tunes to come out.
I'm also getting to the point where I want my own PCB with things on it that I want to play with too.
Kevin
No comments:
Post a Comment