Using Yamaha Sound Chips as Audio Device for Arduino
The purpose of this project is to provide an Arduino compatible library, very simple to use and with the minimum need of additional components to work with oldschool Yamaha Sound Chips.
The library does not aim to use the sound chips to their full potential, but rather to allow their use in third-party projects without difficulty.
If you are interested about more advanced use of the YM2149 (SSG) chip, like streaming, reading SNDH files, I recommend the work of FlorentFlament :
These works really help me at the beginning of the project.
Sound Chip | Work in progress | Works |
---|---|---|
YM2149 (SSG) | ⚪ | ✅ |
YM2203 (OPN) | ✅ | ❌ |
YM2151 (OPM) | ❌ | ❌ |
Sound Chip | Write to chip | Music (A/B/C) | Noise | Mixer | Level control | Envelope |
---|---|---|---|---|---|---|
YM2149 (SSG) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Sound Chip | OPN as SSG |
---|---|
YM2203 (OPN) | ❌ |
Sound Chip | Common name | Other chip required | Do I have it ? |
---|---|---|---|
YM2149F | SSG | ⚪ | ✅ |
YM3526 | OPL | YM3014 | Awaiting delivery |
YM2413 | OPLL | Awaiting delivery | |
Y8950 | MSX-AUDIO | YM3014 | ❌ |
YM3812 | OPL2 | YM3014 | Awaiting delivery |
YMF262 | OPL3 | YAC512 | ❌ |
YMF289 | OPL3-L | ❔ | ❌ |
YMF278 | OPL4 | ❔ | ❌ |
YM2203 | OPN | YM3014 | ✅ |
YM2608 | OPNA | YM3016 | Awaiting delivery |
YM2610 | OPNB | YM3016 | Awaiting delivery |
YM2612 | OPN2 | ❌ | |
YM3438 | OPN2C | Awaiting delivery | |
YMF276 | OPN2L | DAC ❔ | ❌ |
YMF288 | OPN3 | DAC ❔ | ❌ |
YM2151 | OPM | YM3012 | ✅ |
YM2164 | OPP | YM3012 ❔ | ❌💲 |
YM2154 | RYP4 | ❔ | ❌ |
YM2414 | OPZ | ❔ | ❌ |
Chip | Other name | Usage | Do I have it ? |
---|---|---|---|
YM3014 | Y3014B | DAC | ✅ |
YAC512 | DAC | ❌ | |
YM3016 | YM3016-D | DAC | Awaiting delivery |
YM3012 | DAC | ✅ |
To play and store a song, we have to convert a music score into C arrays which can be interpreted by the program. A Python script to transform a channel from a midi file into an array will be available soon.
At first, a song is loaded, then we need to call an update function (song_next_iteration()) to update the SSG register.
For now, we want an SSG song to command the following elements.
- 3 square wave channel (note and duration)
- 1 noise (frequency and duration)
For each channel, we use two arrays, one for note and one for duration.
The note array is read when the previous note has finished and we update the channel frequency with it's information. If value is 0, the channel is muted
The duration array is read when the previous note has finished and we update the couter (which is decremented every time the update function is called) with the value in the duration array.
For memory reason, we use char (8 bits) to store thoses information, but if a duration is longer than 255 call, we can use a word (16 bits) using 3 cases of the array, one for a 0 flag and two for 16 bits duration.
{0, 0x01, 0x90} // 400 call duration.
The tempo is set by the delay between each call to the update function. For this reason, the duration has no absolute relation with "note value", if the update is called 4 times per BPM, a "4" duration is a 𝅘𝅥 but if the update is called 8 times per BPM, it's a 𝅘𝅥𝅮.
In our future example, we use the following table as default
Duration | Equivalent in note value |
---|---|
1 | 𝅘𝅥𝅲 |
2 | 𝅘𝅥𝅱 |
4 | 𝅘𝅥𝅰 |
8 | 𝅘𝅥𝅯 |
16 | 𝅘𝅥𝅮 |
32 | 𝅘𝅥 |
48 | 𝅘𝅥. |
64 | 𝅗𝅥 |
96 | 𝅗𝅥. |
128 | 𝅝 |
256 (0,1,0) | 𝅝𝆊𝅝 |