Introduction: Arduino Chiptunes
I am not a big fan of programming. I may like it, but I try to keep it down to a 555 timer, like in this ible. However, when it comes to synthesizers, I kinda NEED programming. One day, while exploring Synthesizers, I came across something known as chiptunes.
I decided to listen and found the music really awesome. So I tried to make it with Arduino. Didn't work. The code was not in the Arduino language. Some browsing of the internet forums led me to find that an Arduino forum user by the name of Stimmer had ported Linus Åkesson's Hardware Chiptune Project to the Arduino.
And now, here is that project.
Step 1: Gains and Losses.
You will gain:
A machine capable of playing a tune every time you push a button to activate it.
You will lose: (Materials)
An Arduino
A speaker+possibly an amp
2 jumper wires (If your speaker doesn't already have them)
Step 2: What Type of Speakers Should I Use?
There are many speakers available on the market these days.
Some have 3.5 mm jacks and amplifiers. Too bad you probably want to save them for your phone.
Some have wire, coils and a magnet - exposed.
Heck, you could even use headphones - assuming that it isn't haphazardly held together by Sugru or some other similar substance.
Step 3: Programming.
Start by copying the code from below.
Then, copy and paste the code into the Arduino editor.
Compile. Upload.
You're done with the software.
<pre>#include <avr/io.h> #include <avr/signal.h> #include <avr/interrupt.h> #define TRACKLEN 32 #define MAXTRACK 0x92 #define SONGLEN 0x37 #include <avr/pgmspace.h> extern "C" { typedef unsigned char u8; typedef unsigned short u16; typedef char s8; typedef short s16; typedef unsigned long u32; const byte songdata[] PROGMEM = { 0x77, 0x00, 0x2a, 0x84, 0x05, 0xbb, 0x90, 0x18, 0x40, 0xc3, 0x6c, 0x50, 0x0e, 0xd1, 0x41, 0x3e, 0x4c, 0x08, 0x1a, 0xb1, 0x24, 0xa4, 0x44, 0x99, 0x30, 0x13, 0x67, 0x82, 0x50, 0x60, 0x0a, 0x53, 0x11, 0x2c, 0x9e, 0x45, 0xbe, 0xe8, 0x18, 0x2b, 0xe3, 0x68, 0x9c, 0x8d, 0xc2, 0xd1, 0x39, 0x60, 0xc7, 0xf5, 0xe0, 0x1f, 0x1c, 0x64, 0x88, 0x84, 0x11, 0x3c, 0xb2, 0x48, 0x4e, 0x49, 0x2d, 0x89, 0x26, 0xe8, 0x84, 0x9f, 0x38, 0x94, 0x8e, 0x92, 0x53, 0x9e, 0x4a, 0x57, 0xb1, 0x2b, 0x8d, 0x45, 0xb5, 0x14, 0x97, 0xe9, 0x32, 0x5e, 0xe8, 0x0b, 0x83, 0x29, 0x31, 0x40, 0xc6, 0xcc, 0x28, 0x9a, 0x51, 0x63, 0x6c, 0xaa, 0xcd, 0xb8, 0xe1, 0x37, 0x10, 0x27, 0xe5, 0x54, 0x1d, 0xb9, 0x73, 0x7a, 0x72, 0x4f, 0xf2, 0xc9, 0x3e, 0x03, 0x08, 0x00, 0x00, 0x1b, 0x00, 0x00, 0xa8, 0xb5, 0x00, 0x80, 0x9b, 0x2b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x20, 0x90, 0x18, 0x00, 0x02, 0x09, 0x00, 0x20, 0x90, 0x00, 0x4c, 0x02, 0x09, 0x00, 0x25, 0x90, 0x00, 0x54, 0x0e, 0x09, 0xc0, 0x24, 0x90, 0x00, 0x4c, 0x02, 0x09, 0x00, 0x25, 0x90, 0x00, 0x54, 0x02, 0x09, 0xc2, 0x24, 0x90, 0x50, 0x4c, 0x02, 0x89, 0x05, 0x25, 0x90, 0x60, 0x54, 0x02, 0x89, 0xc6, 0x24, 0x90, 0x70, 0x4c, 0x02, 0x89, 0x07, 0x25, 0x90, 0x90, 0x5c, 0x0e, 0x89, 0xc6, 0x44, 0x60, 0xc2, 0x7c, 0x04, 0xa7, 0x0c, 0x48, 0x80, 0xd2, 0x84, 0x04, 0xa9, 0x8d, 0x48, 0x88, 0x3e, 0xce, 0x48, 0xa0, 0xea, 0x90, 0x04, 0x2c, 0x4f, 0xe9, 0xb0, 0x1a, 0x10, 0x02, 0x89, 0xd7, 0x24, 0x90, 0x80, 0x51, 0x02, 0x89, 0x58, 0x25, 0x90, 0x90, 0x4d, 0x02, 0x89, 0xd9, 0x24, 0x90, 0xa0, 0x51, 0x02, 0x89, 0xd8, 0xe5, 0x90, 0x90, 0x4d, 0x82, 0xef, 0x74, 0xe6, 0x34, 0x82, 0xf0, 0x7c, 0x26, 0x35, 0x84, 0xf1, 0x64, 0x66, 0x35, 0x82, 0xf2, 0x6c, 0xe6, 0x34, 0x82, 0xc9, 0x84, 0xe6, 0x34, 0x82, 0xc9, 0x84, 0x26, 0x35, 0x84, 0xc9, 0x8c, 0x66, 0x35, 0x8e, 0xc9, 0x8c, 0xe6, 0x34, 0x8c, 0xf8, 0xcc, 0xe7, 0x34, 0x8c, 0xf8, 0xcc, 0x27, 0x35, 0x8c, 0xf8, 0xcc, 0x67, 0x35, 0x8c, 0xf8, 0xcc, 0xe7, 0x34, 0x8c, 0xf8, 0xcc, 0xe7, 0x34, 0x8c, 0xf8, 0xcc, 0x27, 0x35, 0x88, 0xed, 0xcc, 0xe7, 0x35, 0x90, 0xee, 0x28, 0x5a, 0x03, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x02, 0x01, 0x90, 0x0b, 0x31, 0x05, 0xa0, 0x02, 0xf0, 0x00, 0x08, 0xff, 0x09, 0x03, 0x07, 0x02, 0x09, 0x02, 0x0b, 0x31, 0x01, 0x70, 0x05, 0xd0, 0x07, 0x02, 0x02, 0xf8, 0x04, 0x01, 0x00, 0x09, 0x02, 0x06, 0x05, 0x0b, 0x31, 0x08, 0xff, 0x02, 0xf0, 0x07, 0x06, 0x02, 0x00, 0x07, 0x16, 0x0a, 0x25, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x00, 0x06, 0x05, 0x0b, 0x3d, 0x02, 0xf0, 0x07, 0x06, 0x02, 0x00, 0x07, 0x20, 0x02, 0xf0, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x02, 0x01, 0x50, 0x06, 0x01, 0x0b, 0x31, 0x07, 0x05, 0x02, 0xfe, 0x00, 0x09, 0x02, 0x01, 0x80, 0x0b, 0x3d, 0x08, 0xc0, 0x02, 0x08, 0x07, 0x02, 0x02, 0xf0, 0x07, 0x02, 0x02, 0x00, 0x07, 0x16, 0x0a, 0x34, 0x00, 0x09, 0x03, 0x08, 0xff, 0x02, 0xfc, 0x00, 0x03, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x02, 0x06, 0x05, 0x02, 0xff, 0x0b, 0x3d, 0x07, 0x03, 0x0b, 0x38, 0x07, 0x03, 0x0b, 0x34, 0x07, 0x03, 0x0b, 0x31, 0x07, 0x03, 0x04, 0x07, 0x00, 0x03, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x02, 0x06, 0x05, 0x02, 0xff, 0x0b, 0x3d, 0x07, 0x03, 0x0b, 0x38, 0x07, 0x03, 0x0b, 0x35, 0x07, 0x03, 0x0b, 0x31, 0x07, 0x03, 0x04, 0x07, 0x00, 0x03, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x02, 0x06, 0x05, 0x02, 0xff, 0x0b, 0x3d, 0x07, 0x03, 0x0b, 0x38, 0x07, 0x03, 0x0b, 0x36, 0x07, 0x03, 0x0b, 0x31, 0x07, 0x03, 0x04, 0x07, 0x00, 0x09, 0x03, 0x08, 0xff, 0x07, 0x01, 0x09, 0x00, 0x06, 0x05, 0x0b, 0x3d, 0x02, 0xf0, 0x07, 0x06, 0x02, 0x00, 0x07, 0x06, 0x0a, 0x25, 0x00, 0x09, 0x03, 0x08, 0xff, 0x02, 0xf0, 0x00, 0x08, 0xc4, 0x09, 0x00, 0x06, 0x05, 0x0b, 0x3d, 0x02, 0xf0, 0x07, 0x06, 0x02, 0x00, 0x07, 0x06, 0x0a, 0x25, 0x00, 0x00, 0x00, 0x6b, 0x04, 0x00, 0x20, 0x0d, 0x2c, 0x23, 0x58, 0x23, 0x00, 0xb4, 0x81, 0x80, 0x44, 0xc0, 0x34, 0x90, 0x06, 0xd2, 0xc0, 0x32, 0x02, 0x60, 0x8d, 0x40, 0x1a, 0x00, 0x11, 0x00, 0x6b, 0x04, 0xd2, 0x00, 0x00, 0x2c, 0x23, 0x00, 0x80, 0x35, 0x02, 0x00, 0x90, 0x06, 0x80, 0x65, 0x04, 0x00, 0x00, 0x00, 0x94, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x8b, 0x45, 0x62, 0x89, 0x25, 0x96, 0x18, 0x12, 0x03, 0x90, 0x18, 0xc2, 0x42, 0x58, 0x00, 0xc2, 0x42, 0x4a, 0x48, 0x09, 0x01, 0x21, 0x20, 0x00, 0x2b, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x08, 0x11, 0x6a, 0x8d, 0x48, 0x23, 0x0d, 0xa4, 0x81, 0x65, 0x84, 0x08, 0xd0, 0x0c, 0x42, 0x11, 0x01, 0xd6, 0x08, 0xa4, 0x81, 0x65, 0x84, 0x08, 0xb5, 0x46, 0x20, 0x0d, 0xa4, 0x81, 0x65, 0x84, 0x08, 0xb5, 0x46, 0x20, 0x8d, 0x34, 0xd2, 0x48, 0x03, 0x6b, 0x04, 0x00, 0x20, 0x0d, 0x2c, 0x23, 0x44, 0x80, 0x35, 0x02, 0x40, 0x1b, 0x08, 0x98, 0xc6, 0x32, 0x42, 0x04, 0x58, 0x23, 0x90, 0x06, 0xd2, 0xc0, 0x32, 0x82, 0x35, 0x02, 0xcb, 0x08, 0x11, 0x20, 0x19, 0x22, 0x00, 0x6b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xd1, 0x00, 0x20, 0x25, 0x84, 0x86, 0xcc, 0x90, 0x12, 0x12, 0x43, 0x6c, 0xc8, 0x0c, 0xa1, 0x21, 0x25, 0x84, 0x86, 0xcc, 0x90, 0x12, 0x12, 0x43, 0x66, 0xc8, 0x0b, 0xc7, 0xcd, 0x00, 0x00, 0x44, 0x60, 0x00, 0x48, 0x0c, 0x20, 0x02, 0x03, 0x40, 0x70, 0x44, 0x20, 0x22, 0x00, 0x82, 0x23, 0x02, 0x24, 0x07, 0x10, 0x01, 0x62, 0x23, 0x02, 0xa4, 0x87, 0xdc, 0x00, 0xc3, 0x0d, 0x40, 0x04, 0x06, 0x80, 0xc4, 0x00, 0x22, 0x30, 0x00, 0x04, 0x87, 0xd8, 0x10, 0x1a, 0x11, 0x20, 0x31, 0x80, 0x08, 0x10, 0x1a, 0x11, 0x20, 0xb8, 0xe4, 0xb2, 0x4b, 0x0f, 0x03, 0x0e, 0x80, 0x00, 0x91, 0x1e, 0x80, 0xe4, 0x10, 0x21, 0x02, 0x44, 0x7e, 0x08, 0x10, 0x11, 0x22, 0x3b, 0x00, 0xc1, 0x21, 0x33, 0xc4, 0x06, 0x03, 0x0e, 0xf2, 0x03, 0x11, 0x00, 0xe9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x28, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x11, 0x20, 0x33, 0x22, 0x30, 0x10, 0x1a, 0x88, 0x00, 0x48, 0x8c, 0x08, 0x1c, 0x1b, 0x11, 0x20, 0x31, 0x22, 0x40, 0x70, 0xb1, 0x85, 0x86, 0xe0, 0x88, 0x00, 0xb1, 0x21, 0x34, 0x22, 0x40, 0x66, 0x44, 0x80, 0xc4, 0x00, 0x00, 0x63, 0x0d, 0xe7, 0x3f, 0x25, 0x24, 0x86, 0x94, 0x10, 0x16, 0x52, 0x42, 0x62, 0x1c, 0x80, 0x94, 0x10, 0x5a, 0x66, 0x89, 0x85, 0x15, 0x55, 0x50, 0x29, 0x05, 0x14, 0x4e, 0x3c, 0xf9, 0x04, 0x94, 0x52, 0x5a, 0x89, 0x05, 0x07, 0x2b, 0x0d, 0xe7, 0x3f, 0x19, 0x24, 0x83, 0xa0, 0x90, 0x0c, 0x52, 0x42, 0x58, 0xc8, 0x08, 0x29, 0x21, 0x19, 0x24, 0x83, 0xa0, 0x90, 0x17, 0xc2, 0xe2, 0x00, 0xa4, 0x94, 0x53, 0x50, 0x61, 0x01, 0x63, 0x0d, 0xe7, 0x3f, 0x31, 0x24, 0x83, 0xb0, 0x90, 0x12, 0x92, 0x41, 0x58, 0x44, 0x80, 0xc6, 0x06, 0x80, 0x08, 0x90, 0x0c, 0xe7, 0x3f, 0x2c, 0xa4, 0x84, 0x64, 0x90, 0x12, 0x92, 0xe1, 0x00, 0x84, 0x45, 0x04, 0xc8, 0x8b, 0x08, 0x00, 0xa3, 0x0d, 0x11, 0x20, 0x36, 0x00, 0xc1, 0x11, 0x01, 0x81, 0xd0, 0x90, 0x5d, 0x72, 0xc1, 0x21, 0x3b, 0xa4, 0x87, 0xfc, 0x88, 0x00, 0x11, 0x02, 0x08, 0x0e, 0x99, 0x21, 0x36, 0x00, 0x6b, 0x14, 0x12, 0x00, 0x00, 0x80, 0x08, 0x00, 0xc4, 0x10, 0x44, 0x16, 0x48, 0x03, 0x09, 0x20, 0x01, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x46, 0x01, 0x44, 0x40, 0x20, 0x0d, 0x10, 0x01, 0x90, 0x06, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x14, 0x00, 0x20, 0x15, 0x10, 0x01, 0x10, 0x08, 0x92, 0x40, 0x0c, 0xc8, 0x02, 0x59, 0x00, 0x44, 0x80, 0x20, 0x88, 0xc0, 0x40, 0x06, 0x44, 0x60, 0x20, 0x08, 0x00, 0x0b, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b, 0x14, 0x00, 0x20, 0x15, 0x10, 0x01, 0x10, 0x08, 0x92, 0x40, 0x0c, 0x08, 0x02, 0x41, 0x00, 0x44, 0x80, 0x20, 0x88, 0xc0, 0x40, 0x06, 0x44, 0x60, 0x20, 0x08, 0x00, 0x43, 0x19, 0x00, 0x00, 0x00, 0x80, 0x8c, 0x40, 0x04, 0x40, 0x56, 0x00, 0x40, 0x04, 0x88, 0x0a, 0x00, 0x88, 0x00, 0x41, 0x01, 0x44, 0x00, 0x3b, 0x19, 0x00, 0x10, 0x01, 0x82, 0x22, 0x02, 0x44, 0x05, 0x00, 0x00, 0x00, 0x4a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x19, 0x00, 0x00, 0x00, 0x80, 0x98, 0x00, 0x04, 0x05, 0x00, 0x00, 0x42, 0x02, 0x58, 0xf8, 0x00, 0x00, 0x1b, 0x19, 0x00, 0x00, 0xa0, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x19, 0x80, 0x9c, 0x10, 0x14, 0x80, 0xa0, 0x10, 0x15, 0x00, 0x0a, 0x3e, 0x80, 0xb4, 0x00, 0xa4, 0x85, 0xa8, 0x88, 0x00, 0x21, 0x11, 0x01, 0x32, 0x22, 0x02, 0x00, 0x43, 0x19, 0x00, 0x0a, 0x3f, 0x80, 0xac, 0x00, 0x50, 0xf8, 0x01, 0x44, 0x05, 0x80, 0xc2, 0x0f, 0x20, 0x26, 0xb0, 0x06, 0x00, 0x00, 0x43, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x14, 0x80, 0x10, 0x10, 0x08, 0x80, 0x2c, 0x10, 0x07, 0xf2, 0x40, 0x08, 0x00, 0x42, 0x40, 0x20, 0x00, 0xb2, 0x40, 0x20, 0x81, 0x64, 0x11, 0x02, 0x5b, 0x14, 0x80, 0x2c, 0x90, 0x0b, 0x80, 0x18, 0x90, 0x04, 0xa2, 0x40, 0x16, 0x00, 0xd2, 0x00, 0x10, 0x07, 0x80, 0x3c, 0x58, 0xe0, 0x00, 0x63, 0x14, 0xc2, 0x00, 0x11, 0x00, 0x39, 0x20, 0x0a, 0x84, 0x01, 0x22, 0x00, 0xa2, 0x40, 0x18, 0x08, 0x03, 0x44, 0x00, 0xe4, 0x80, 0x3c, 0x10, 0x07, 0xc2, 0x40, 0x0a, 0x00, 0x3b, 0x14, 0x72, 0x00, 0x90, 0x09, 0x80, 0x40, 0x10, 0x07, 0xb2, 0x40, 0x12, 0x39, 0xe4, 0x92, 0x4a, 0x26, 0x81, 0xc4, 0x91, 0x44, 0x0e, 0x00, 0x92, 0x00, 0x00, 0x5b, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, 0x00, 0x80, 0x30, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x20, 0x0e, 0x00, 0x83, 0x14, 0x00, 0x20, 0x10, 0x10, 0x01, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x1d, 0x00, 0x00, 0x00, 0x86, 0x42, 0x00, 0x41, 0x11, 0x01, 0x82, 0x02, 0x00, 0x84, 0x46, 0x04, 0x04, 0x42, 0x23, 0x02, 0x04, 0x05, 0x00, 0x1b, 0x25, 0x00, 0x10, 0x01, 0xf2, 0x22, 0x02, 0x02, 0x19, 0x01, 0xc8, 0x88, 0x08, 0x90, 0x17, 0x80, 0x8c, 0x00, 0x64, 0x84, 0xbc, 0x90, 0x11, 0x00, 0x23, 0x25, 0x00, 0x10, 0x01, 0x02, 0x23, 0x02, 0x02, 0x21, 0x01, 0x08, 0x89, 0x08, 0x10, 0x12, 0x00, 0x20, 0x30, 0x22, 0x20, 0x10, 0x18, 0x11, 0x20, 0x24, 0x00, 0x5b, 0x25, 0x11, 0x20, 0x1f, 0x80, 0x08, 0x90, 0x15, 0x11, 0x20, 0x2b, 0x22, 0x40, 0x3e, 0x00, 0x11, 0x20, 0x2b, 0x22, 0x40, 0x56, 0x44, 0x80, 0x7c, 0x00, 0x22, 0x40, 0x3e, 0x00, 0xb2, 0x22, 0x02, 0x02, 0xf9, 0x10, 0x01, 0x00, 0x23, 0x25, 0x80, 0x90, 0x88, 0x00, 0x81, 0x81, 0x08, 0x80, 0xc0, 0x88, 0x00, 0x21, 0x01, 0x88, 0x09, 0x44, 0x00, 0xc4, 0x44, 0x04, 0x88, 0x0c, 0x20, 0x02, 0x44, 0x46, 0x04, 0x88, 0x89, 0x08, 0x10, 0x19, 0x11, 0x00, 0xa3, 0x25, 0x42, 0x0b, 0x2d, 0x34, 0x22, 0x40, 0x50, 0x20, 0x02, 0x20, 0x28, 0x22, 0x40, 0x50, 0x08, 0x0d, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x29, 0x42, 0x0b, 0x2a, 0x34, 0x22, 0x40, 0x50, 0x20, 0x02, 0x20, 0x28, 0x22, 0x40, 0x50, 0x00, 0x42, 0x43, 0x68, 0xa1, 0x85, 0x46, 0x04, 0x08, 0x0a, 0x44, 0x00, 0x04, 0x45, 0x04, 0x08, 0x0a, 0xa1, 0x01, 0x0b, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x1d, 0x00, 0x00, 0x18, 0x6b, 0x91, 0x12, 0x80, 0xb0, 0x90, 0x18, 0xc2, 0x42, 0x4a, 0x08, 0x8d, 0x02, 0x90, 0x19, 0xc2, 0x42, 0x4a, 0x88, 0x0d, 0xc3, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0c, 0xa1, 0xc5, 0x96, 0x5b, 0x70, 0xd9, 0xa5, 0x07, 0x03, 0x2e, 0x00, 0x00, 0x48, 0x0f, 0x40, 0x80, 0x00, 0xf2, 0x23, 0x02, 0x64, 0x07, 0x22, 0x00, 0x82, 0x03, 0x10, 0x1c, 0x11, 0x20, 0x3f, 0x64, 0x07, 0x03, 0x6e, 0x7e, 0xe9, 0x05, 0x17, 0x5a, 0x66, 0xb1, 0x65, 0x16, 0x5a, 0x66, 0x89, 0x85, 0x95, 0x57, 0x54, 0x99, 0xe5, 0x95, 0x18, 0x11, 0x20, 0x31, 0x22, 0x40, 0x62, 0x44, 0x80, 0xc4, 0x00, 0x22, 0x40, 0x62, 0x44, 0x80, 0xf4, 0x88, 0x00, 0x89, 0x11, 0x01, 0x00, 0x03, 0x2e, 0x12, 0x43, 0x62, 0x48, 0x0f, 0x89, 0x21, 0x3d, 0x44, 0x18, 0x60, 0x7e, 0x08, 0x10, 0x89, 0x21, 0x31, 0xa4, 0x87, 0xc4, 0x90, 0x1e, 0x22, 0x0c, 0x30, 0x3f, 0x00, 0xfb, 0x2d, 0x02, 0x24, 0x02, 0x84, 0x48, 0x04, 0x48, 0x6f, 0xc4, 0x9e, 0x70, 0xd7, 0xeb, 0xc5, 0x76, 0xc2, 0x1e, 0x70, 0x17, 0xeb, 0xe1, 0x76, 0xc0, 0xde, 0x6f, 0xd7, 0xeb, 0xc5, 0x76, 0xbf, 0xde, 0x6e, 0x17, 0xeb, 0xe1, 0x76, 0xbb, 0x1e, 0x6e, 0x87, 0xeb, 0xc5, 0x76, 0xc4, 0xde, 0x6e, 0x17, 0xeb, 0xf5, 0x36, 0x3f, 0xa3, 0x0d, 0x00, 0x00, 0x48, 0x0c, 0x40, 0x68, 0x00, 0x32, 0x03, 0x00, 0x00, 0x89, 0x01, 0xc8, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbd, 0x26, 0xb1, 0xcc, 0x00, 0x7f, 0x4d, 0x41, 0x00, 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x21, 0x11, 0x20, 0x25, 0x80, 0x08, 0x90, 0x58, 0x62, 0x29, 0x11, 0x01, 0x52, 0x22, 0x02, 0x00, 0x29, 0x11, 0x01, 0x56, 0x52, 0x22, 0x00, 0x90, 0x12, 0x11, 0x60, 0x25, 0x24, 0x02, 0x00, 0x29, 0x11, 0x01, 0x52, 0x22, 0x02, 0xa4, 0x44, 0x04, 0x00, 0xcf, 0xcc, 0xc0, 0x11, 0x01, 0x52, 0x22, 0x02, 0xa4, 0x44, 0x04, 0x48, 0x86, 0x08, 0x90, 0x12, 0x11, 0x20, 0x19, 0x22, 0x40, 0x4a, 0x44, 0x80, 0x94, 0x88, 0x00, 0xc9, 0x10, 0x01, 0x52, 0x22, 0x02, 0xa4, 0x44, 0x04, 0x48, 0x86, 0x08, 0x90, 0x12, 0x11, 0xa0, 0x99, 0x01, 0x68, 0x46, 0x80, 0x53, 0x22, 0x02, 0xa4, 0x44, 0x04 }; volatile u8 callbackwait; volatile u8 lastsample; volatile u8 timetoplay; volatile u8 test; volatile u8 testwait; u8 trackwait; u8 trackpos; u8 playsong; u8 songpos; u32 noiseseed = 1; u8 light[2]; /*const u16 freqtable[] = { 0x010b, 0x011b, 0x012c, 0x013e, 0x0151, 0x0165, 0x017a, 0x0191, 0x01a9, 0x01c2, 0x01dd, 0x01f9, 0x0217, 0x0237, 0x0259, 0x027d, 0x02a3, 0x02cb, 0x02f5, 0x0322, 0x0352, 0x0385, 0x03ba, 0x03f3, 0x042f, 0x046f, 0x04b2, 0x04fa, 0x0546, 0x0596, 0x05eb, 0x0645, 0x06a5, 0x070a, 0x0775, 0x07e6, 0x085f, 0x08de, 0x0965, 0x09f4, 0x0a8c, 0x0b2c, 0x0bd6, 0x0c8b, 0x0d4a, 0x0e14, 0x0eea, 0x0fcd, 0x10be, 0x11bd, 0x12cb, 0x13e9, 0x1518, 0x1659, 0x17ad, 0x1916, 0x1a94, 0x1c28, 0x1dd5, 0x1f9b, 0x217c, 0x237a, 0x2596, 0x27d3, 0x2a31, 0x2cb3, 0x2f5b, 0x322c, 0x3528, 0x3851, 0x3bab, 0x3f37, 0x42f9, 0x46f5, 0x4b2d, 0x4fa6, 0x5462, 0x5967, 0x5eb7, 0x6459, 0x6a51, 0x70a3, 0x7756, 0x7e6f };*/ const u16 freqtable[] = { 0x0085, 0x008d, 0x0096, 0x009f, 0x00a8, 0x00b2, 0x00bd, 0x00c8, 0x00d4, 0x00e1, 0x00ee, 0x00fc, 0x010b, 0x011b, 0x012c, 0x013e, 0x0151, 0x0165, 0x017a, 0x0191, 0x01a9, 0x01c2, 0x01dd, 0x01f9, 0x0217, 0x0237, 0x0259, 0x027d, 0x02a3, 0x02cb, 0x02f5, 0x0322, 0x0352, 0x0385, 0x03ba, 0x03f3, 0x042f, 0x046f, 0x04b2, 0x04fa, 0x0546, 0x0596, 0x05eb, 0x0645, 0x06a5, 0x070a, 0x0775, 0x07e6, 0x085f, 0x08de, 0x0965, 0x09f4, 0x0a8c, 0x0b2c, 0x0bd6, 0x0c8b, 0x0d4a, 0x0e14, 0x0eea, 0x0fcd, 0x10be, 0x11bd, 0x12cb, 0x13e9, 0x1518, 0x1659, 0x17ad, 0x1916, 0x1a94, 0x1c28, 0x1dd5, 0x1f9b, 0x217c, 0x237a, 0x2596, 0x27d3, 0x2a31, 0x2cb3, 0x2f5b, 0x322c, 0x3528, 0x3851, 0x3bab, 0x3f37 }; const s8 sinetable[] = { 0, 12, 25, 37, 49, 60, 71, 81, 90, 98, 106, 112, 117, 122, 125, 126, 127, 126, 125, 122, 117, 112, 106, 98, 90, 81, 71, 60, 49, 37, 25, 12, 0, -12, -25, -37, -49, -60, -71, -81, -90, -98, -106, -112, -117, -122, -125, -126, -127, -126, -125, -122, -117, -112, -106, -98, -90, -81, -71, -60, -49, -37, -25, -12 }; const u8 validcmds[] = "0dfijlmtvw~+="; enum { WF_TRI, WF_SAW, WF_PUL, WF_NOI }; volatile struct oscillator { u16 freq; u16 phase; u16 duty; u8 waveform; u8 volume; // 0-255 } osc[4]; struct trackline { u8 note; u8 instr; u8 cmd[2]; u8 param[2]; }; struct track { struct trackline line[TRACKLEN]; }; struct unpacker { u16 nextbyte; u8 buffer; u8 bits; }; struct channel { struct unpacker trackup; u8 tnum; s8 transp; u8 tnote; u8 lastinstr; u8 inum; u16 iptr; u8 iwait; u8 inote; s8 bendd; s16 bend; s8 volumed; s16 dutyd; u8 vdepth; u8 vrate; u8 vpos; s16 inertia; u16 slur; } channel[4]; u16 resources[16 + MAXTRACK]; struct unpacker songup; byte readsongbyte(u16 offset) { return pgm_read_byte_near(&songdata[0] + offset); } void watchdogoff() { } void initup(struct unpacker *up, u16 offset) { up->nextbyte = offset; up->bits = 0; } u8 readbit(struct unpacker *up) { u8 val; if(!up->bits) { up->buffer = readsongbyte(up->nextbyte++); up->bits = 8; } up->bits--; val = up->buffer & 1; up->buffer >>= 1; return val; } u16 readchunk(struct unpacker *up, u8 n) { u16 val = 0; u8 i; for(i = 0; i < n; i++) { if(readbit(up)) { val |= (1 << i); } } return val; } void readinstr(byte num, byte pos, byte *dest) { dest[0] = readsongbyte(resources[num] + 2 * pos + 0); dest[1] = readsongbyte(resources[num] + 2 * pos + 1); } void runcmd(u8 ch, u8 cmd, u8 param) { switch(validcmds[cmd]) { case '0': channel[ch].inum = 0; break; case 'd': osc[ch].duty = param << 8; break; case 'f': channel[ch].volumed = param; break; case 'i': channel[ch].inertia = param << 1; break; case 'j': channel[ch].iptr = param; break; case 'l': channel[ch].bendd = param; break; case 'm': channel[ch].dutyd = param << 6; break; case 't': channel[ch].iwait = param; break; case 'v': osc[ch].volume = param; break; case 'w': osc[ch].waveform = param; break; case '+': channel[ch].inote = param + channel[ch].tnote - 12 * 4; break; case '=': channel[ch].inote = param; break; case '~': if(channel[ch].vdepth != (param >> 4)) { channel[ch].vpos = 0; } channel[ch].vdepth = param >> 4; channel[ch].vrate = param & 15; break; } } void playroutine() { // called at 50 Hz u8 ch; u8 lights; if(playsong) { if(trackwait) { trackwait--; } else { trackwait = 4; if(!trackpos) { if(playsong) { if(songpos >= SONGLEN) { playsong = 0; } else { for(ch = 0; ch < 4; ch++) { u8 gottransp; u8 transp; gottransp = readchunk(&songup, 1); channel[ch].tnum = readchunk(&songup, 6); if(gottransp) { transp = readchunk(&songup, 4); if(transp & 0x8) transp |= 0xf0; } else { transp = 0; } channel[ch].transp = (s8) transp; if(channel[ch].tnum) { initup(&channel[ch].trackup, resources[16 + channel[ch].tnum - 1]); } } songpos++; } } } if(playsong) { for(ch = 0; ch < 4; ch++) { if(channel[ch].tnum) { u8 note, instr, cmd, param; u8 fields; fields = readchunk(&channel[ch].trackup, 3); note = 0; instr = 0; cmd = 0; param = 0; if(fields & 1) note = readchunk(&channel[ch].trackup, 7); if(fields & 2) instr = readchunk(&channel[ch].trackup, 4); if(fields & 4) { cmd = readchunk(&channel[ch].trackup, 4); param = readchunk(&channel[ch].trackup, 8); } if(note) { channel[ch].tnote = note + channel[ch].transp; if(!instr) instr = channel[ch].lastinstr; } if(instr) { if(instr == 2) light[1] = 5; if(instr == 1) { light[0] = 5; if(channel[ch].tnum == 4) { light[0] = light[1] = 3; } } if(instr == 7) { light[0] = light[1] = 30; } channel[ch].lastinstr = instr; channel[ch].inum = instr; channel[ch].iptr = 0; channel[ch].iwait = 0; channel[ch].bend = 0; channel[ch].bendd = 0; channel[ch].volumed = 0; channel[ch].dutyd = 0; channel[ch].vdepth = 0; } if(cmd) runcmd(ch, cmd, param); } } trackpos++; trackpos &= 31; } } } for(ch = 0; ch < 4; ch++) { s16 vol; u16 duty; u16 slur; while(channel[ch].inum && !channel[ch].iwait) { u8 il[2]; readinstr(channel[ch].inum, channel[ch].iptr, il); channel[ch].iptr++; runcmd(ch, il[0], il[1]); } if(channel[ch].iwait) channel[ch].iwait--; if(channel[ch].inertia) { s16 diff; slur = channel[ch].slur; diff = freqtable[channel[ch].inote] - slur; //diff >>= channel[ch].inertia; if(diff > 0) { if(diff > channel[ch].inertia) diff = channel[ch].inertia; } else if(diff < 0) { if(diff < -channel[ch].inertia) diff = -channel[ch].inertia; } slur += diff; channel[ch].slur = slur; } else { slur = freqtable[channel[ch].inote]; } osc[ch].freq = slur + channel[ch].bend + ((channel[ch].vdepth * sinetable[channel[ch].vpos & 63]) >> 2); channel[ch].bend += channel[ch].bendd; vol = osc[ch].volume + channel[ch].volumed; if(vol < 0) vol = 0; if(vol > 255) vol = 255; osc[ch].volume = vol; duty = osc[ch].duty + channel[ch].dutyd; if(duty > 0xe000) duty = 0x2000; if(duty < 0x2000) duty = 0xe000; osc[ch].duty = duty; channel[ch].vpos += channel[ch].vrate; } lights = 0; if(light[0]) { light[0]--; lights |= 0x04; } if(light[1]) { light[1]--; lights |= 0x10; } PORTB = lights; } void initresources() { u8 i; struct unpacker up; initup(&up, 0); for(i = 0; i < 16 + MAXTRACK; i++) { resources[i] = readchunk(&up, 13); } initup(&songup, resources[0]); } int main() { asm("cli"); watchdogoff(); CLKPR = 0x80; CLKPR = 0x80; DDRC = 0x12; DDRD = 0xff; //PORTC = 0; pinMode(10,OUTPUT); pinMode(12,OUTPUT); timetoplay = 0; trackwait = 0; trackpos = 0; playsong = 1; songpos = 0; osc[0].volume = 0; channel[0].inum = 0; osc[1].volume = 0; channel[1].inum = 0; osc[2].volume = 0; channel[2].inum = 0; osc[3].volume = 0; channel[3].inum = 0; initresources(); TCCR0A = 0x02; TCCR0B = 0x02; // clkIO/8, so 1/8 MHz OCR0A = 125;//125; // 8 KHz TCCR2A=0b10100011; TCCR2B=0b00000001; TIMSK0 = 0x02; asm("sei"); for(;;) { while(!timetoplay); timetoplay--; playroutine(); } } ISR(TIMER0_COMPA_vect) // called at 8 KHz { u8 i; s16 acc; u8 newbit; OCR2B = lastsample; newbit = 0; if(noiseseed & 0x80000000L) newbit ^= 1; if(noiseseed & 0x01000000L) newbit ^= 1; if(noiseseed & 0x00000040L) newbit ^= 1; if(noiseseed & 0x00000200L) newbit ^= 1; noiseseed = (noiseseed << 1) | newbit; if(callbackwait) { callbackwait--; } else { timetoplay++; callbackwait = 180 - 1; } acc = 0; for(i = 0; i < 4; i++) { s8 value; // [-32,31] switch(osc[i].waveform) { case WF_TRI: if(osc[i].phase < 0x8000) { value = -32 + (osc[i].phase >> 9); } else { value = 31 - ((osc[i].phase - 0x8000) >> 9); } break; case WF_SAW: value = -32 + (osc[i].phase >> 10); break; case WF_PUL: value = (osc[i].phase > osc[i].duty)? -32 : 31; break; case WF_NOI: value = (noiseseed & 63) - 32; break; default: value = 0; break; } osc[i].phase += osc[i].freq; acc += value * osc[i].volume; // rhs = [-8160,7905] } // acc [-32640,31620] lastsample = 128 + (acc >> 8); // [1,251] } }
Step 4: Assembly.
You may be done with the software, but the hardware is not finished.
Take one of your speaker wires, and then plug it into pin 3. Then, plug the other end into ground...or 5 volts, your choice.
You should hear music playing.
Now, hit the reset button to here it from the beginning.
Step 5: Done.
Now listen to it play the tune.
Step 6: Adding a Jack.
Keep you eyes peeled for updates...
Step 7: Modulate It!
Step 8: Make It Beatbox.
Here's the beatbox code:
#include #include #include #define TRACKLEN 32 #define MAXTRACK 0x92 #define SONGLEN 0x37 #include extern "C" { typedef unsigned char u8; typedef unsigned short u16; typedef char s8; typedef short s16; typedef unsigned long u32; byte songdata[] PROGMEM = { 0x34,0x80,0x0d,0xe4,0x01,0x41,0x30,0x09,0x34,0x41,0x2c,0xd0,0x05,0xc3,0x80,0x18, 0x14,0x03,0x63,0x70,0x0c,0x90,0x41,0x32,0x50,0x06,0xcb,0xe0,0x1b,0xcc,0x83,0x83, 0xb0,0x11,0x5e,0x42,0x51,0xd8,0x0a,0x6f,0x61,0x30,0x5c,0x86,0xd5,0x40,0x1c,0xbc, 0xc3,0x7d,0x80,0x10,0x02,0x08,0x06,0x40,0x90,0x60,0x00,0x06,0x08,0x06,0x80,0x90, 0x60,0x00,0x0a,0x08,0x06,0xc0,0xb0,0x60,0x00,0x0a,0x08,0x06,0xe0,0xa0,0x68,0x40, 0x02,0x08,0x86,0x43,0x90,0x60,0x00,0x06,0x08,0xc6,0x83,0x90,0x60,0x00,0x0a,0x08, 0x86,0xc3,0xb0,0x60,0x00,0x0a,0x08,0x86,0xe3,0xa0,0x68,0x40,0x09,0x02,0x08,0xa0, 0x01,0x80,0x0b,0x19,0x07,0x04,0x02,0xe8,0x00,0x09,0x00,0x08,0xff,0x0b,0x25,0x02, 0xf0,0x00,0x09,0x03,0x08,0xff,0x07,0x01,0x09,0x02,0x01,0x90,0x0b,0x31,0x05,0xa0, 0x02,0xf0,0x00,0x09,0x03,0x08,0x80,0x02,0xe0,0x00,0x09,0x03,0x08,0xff,0x07,0x01, 0x09,0x02,0x01,0x90,0x0b,0x31,0x05,0xa0,0x07,0x02,0x09,0x03,0x08,0x80,0x02,0xf0, 0x00,0x09,0x01,0x08,0x60,0x0b,0x31,0x02,0xf8,0x00,0x09,0x01,0x08,0xc0,0x0b,0x31, 0x02,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xeb,0x04,0x80,0xa4,0x00, 0xa4,0x03,0x20,0x29,0x00,0xe9,0x00,0x48,0x0a,0x40,0x3a,0x00,0x92,0x02,0x00,0xeb, 0x04,0x80,0xa4,0x00,0xa4,0x03,0x20,0x29,0x00,0xe9,0x00,0x48,0x0a,0x40,0x3e,0x00, 0xb2,0x02,0x00,0x03,0x05,0x80,0xb0,0x00,0x04,0x04,0x20,0x2c,0x00,0x01,0x01,0x08, 0x0b,0x40,0x40,0x00,0xc2,0x02,0x00,0x03,0x05,0x80,0xb0,0x00,0x04,0x04,0x20,0x2c, 0x00,0x01,0x01,0x08,0x0b,0x40,0x44,0x00,0xe2,0x02,0x00,0x23,0x05,0x80,0xc0,0x00, 0x84,0x04,0x20,0x30,0x00,0x21,0x01,0x08,0x0c,0x40,0x48,0x00,0x02,0x03,0x00,0x23, 0x05,0x80,0xc0,0x00,0x84,0x04,0x20,0x30,0x00,0x21,0x01,0x08,0x09,0x81,0x21,0x22, 0x00,0x71,0x21,0x22,0x00,0x23,0x05,0x80,0xc0,0x00,0x84,0x04,0x20,0x30,0x00,0xd9, 0x00,0xc8,0x09,0x40,0x36,0xc8,0x06,0x39,0x21,0x27,0x00,0xab,0x09,0x80,0xc0,0x00, 0x64,0x06,0x20,0x35,0x00,0xa9,0x01,0x08,0x0c,0x40,0x66,0x00,0x52,0x03,0x00,0xab, 0x09,0x80,0xc0,0x00,0x64,0x06,0x20,0x35,0x00,0xa9,0x01,0x08,0x0e,0x40,0x6e,0x00, 0x52,0x03,0x00,0xab,0x09,0x80,0xc0,0x00,0x64,0x06,0x20,0x30,0x00,0xc1,0x01,0xc8, 0x0d,0x40,0x6a,0x00,0x32,0x03,0x00,0xab,0x09,0x80,0xc0,0x00,0x64,0x06,0x20,0x35, 0x00,0xa9,0x01,0x08,0x0c,0x40,0x66,0x00,0x02,0x03,0x00,0xcb,0x0c,0x80,0x95,0x08, 0x29,0x61,0x99,0x02,0xb0,0x12,0x01,0x58,0x66,0xb0,0x12,0x21,0x25,0x00,0xcb,0x14, 0x80,0x95,0x08,0x00,0xcb,0x0c,0x80,0x95,0x08,0x29,0x61,0x99,0x02,0xb0,0x12,0x01, 0x58,0x66,0x90,0x0c,0x80,0x64,0xb0,0x4c,0x21,0x19,0x2c,0x33,0x00,0x00,0x83,0x1d, 0x80,0xc1,0x0c,0x4b,0x1d,0x80,0x9c,0x90,0x14,0x76,0x32,0x0c,0x76,0x00,0x06,0x33, 0x2c,0x75,0x00,0x96,0x32,0x00,0x00,0x63,0x1d,0x80,0xb1,0x0c,0x73,0x1d,0x80,0xc4, 0x10,0x18,0x76,0x32,0x8c,0x75,0x00,0x06,0x33,0xcc,0x75,0x00,0xe6,0x32,0x00,0x00, 0x00,0x00,0x00,0x00,0x30,0xd8,0x01,0x88,0x0b,0x40,0x58,0x00,0x72,0x42,0x52,0x58, 0xca,0x00, }; volatile u8 callbackwait; volatile u8 lastsample; volatile u8 timetoplay; volatile u8 test; volatile u8 testwait; u8 trackwait; u8 trackpos; u8 playsong; u8 songpos; u32 noiseseed = 1; u8 light[2]; /*const u16 freqtable[] = { 0x010b, 0x011b, 0x012c, 0x013e, 0x0151, 0x0165, 0x017a, 0x0191, 0x01a9, 0x01c2, 0x01dd, 0x01f9, 0x0217, 0x0237, 0x0259, 0x027d, 0x02a3, 0x02cb, 0x02f5, 0x0322, 0x0352, 0x0385, 0x03ba, 0x03f3, 0x042f, 0x046f, 0x04b2, 0x04fa, 0x0546, 0x0596, 0x05eb, 0x0645, 0x06a5, 0x070a, 0x0775, 0x07e6, 0x085f, 0x08de, 0x0965, 0x09f4, 0x0a8c, 0x0b2c, 0x0bd6, 0x0c8b, 0x0d4a, 0x0e14, 0x0eea, 0x0fcd, 0x10be, 0x11bd, 0x12cb, 0x13e9, 0x1518, 0x1659, 0x17ad, 0x1916, 0x1a94, 0x1c28, 0x1dd5, 0x1f9b, 0x217c, 0x237a, 0x2596, 0x27d3, 0x2a31, 0x2cb3, 0x2f5b, 0x322c, 0x3528, 0x3851, 0x3bab, 0x3f37, 0x42f9, 0x46f5, 0x4b2d, 0x4fa6, 0x5462, 0x5967, 0x5eb7, 0x6459, 0x6a51, 0x70a3, 0x7756, 0x7e6f };*/ const u16 freqtable[] = { 0x0085, 0x008d, 0x0096, 0x009f, 0x00a8, 0x00b2, 0x00bd, 0x00c8, 0x00d4, 0x00e1, 0x00ee, 0x00fc, 0x010b, 0x011b, 0x012c, 0x013e, 0x0151, 0x0165, 0x017a, 0x0191, 0x01a9, 0x01c2, 0x01dd, 0x01f9, 0x0217, 0x0237, 0x0259, 0x027d, 0x02a3, 0x02cb, 0x02f5, 0x0322, 0x0352, 0x0385, 0x03ba, 0x03f3, 0x042f, 0x046f, 0x04b2, 0x04fa, 0x0546, 0x0596, 0x05eb, 0x0645, 0x06a5, 0x070a, 0x0775, 0x07e6, 0x085f, 0x08de, 0x0965, 0x09f4, 0x0a8c, 0x0b2c, 0x0bd6, 0x0c8b, 0x0d4a, 0x0e14, 0x0eea, 0x0fcd, 0x10be, 0x11bd, 0x12cb, 0x13e9, 0x1518, 0x1659, 0x17ad, 0x1916, 0x1a94, 0x1c28, 0x1dd5, 0x1f9b, 0x217c, 0x237a, 0x2596, 0x27d3, 0x2a31, 0x2cb3, 0x2f5b, 0x322c, 0x3528, 0x3851, 0x3bab, 0x3f37 }; const s8 sinetable[] = { 0, 12, 25, 37, 49, 60, 71, 81, 90, 98, 106, 112, 117, 122, 125, 126, 127, 126, 125, 122, 117, 112, 106, 98, 90, 81, 71, 60, 49, 37, 25, 12, 0, -12, -25, -37, -49, -60, -71, -81, -90, -98, -106, -112, -117, -122, -125, -126, -127, -126, -125, -122, -117, -112, -106, -98, -90, -81, -71, -60, -49, -37, -25, -12 }; const u8 validcmds[] = "0dfijlmtvw~+="; enum { WF_TRI, WF_SAW, WF_PUL, WF_NOI }; volatile struct oscillator { u16 freq; u16 phase; u16 duty; u8 waveform; u8 volume; // 0-255 } osc[4]; struct trackline { u8 note; u8 instr; u8 cmd[2]; u8 param[2]; }; struct track { struct trackline line[TRACKLEN]; }; struct unpacker { u16 nextbyte; u8 buffer; u8 bits; }; struct channel { struct unpacker trackup; u8 tnum; s8 transp; u8 tnote; u8 lastinstr; u8 inum; u16 iptr; u8 iwait; u8 inote; s8 bendd; s16 bend; s8 volumed; s16 dutyd; u8 vdepth; u8 vrate; u8 vpos; s16 inertia; u16 slur; } channel[4]; u16 resources[16 + MAXTRACK]; struct unpacker songup; byte readsongbyte(u16 offset) { return pgm_read_byte_near(&songdata[0] + offset); } void watchdogoff() { } void initup(struct unpacker *up, u16 offset) { up->nextbyte = offset; up->bits = 0; } u8 readbit(struct unpacker *up) { u8 val; if(!up->bits) { up->buffer = readsongbyte(up->nextbyte++); up->bits = 8; } up->bits--; val = up->buffer & 1; up->buffer >>= 1; return val; } u16 readchunk(struct unpacker *up, u8 n) { u16 val = 0; u8 i; for(i = 0; i < n; i++) { if(readbit(up)) { val |= (1 << i); } } return val; } void readinstr(byte num, byte pos, byte *dest) { dest[0] = readsongbyte(resources[num] + 2 * pos + 0); dest[1] = readsongbyte(resources[num] + 2 * pos + 1); } void runcmd(u8 ch, u8 cmd, u8 param) { switch(validcmds[cmd]) { case '0': channel[ch].inum = 0; break; case 'd': osc[ch].duty = param << 8; break; case 'f': channel[ch].volumed = param; break; case 'i': channel[ch].inertia = param << 1; break; case 'j': channel[ch].iptr = param; break; case 'l': channel[ch].bendd = param; break; case 'm': channel[ch].dutyd = param << 6; break; case 't': channel[ch].iwait = param; break; case 'v': osc[ch].volume = param; break; case 'w': osc[ch].waveform = param; break; case '+': channel[ch].inote = param + channel[ch].tnote - 12 * 4; break; case '=': channel[ch].inote = param; break; case '~': if(channel[ch].vdepth != (param >> 4)) { channel[ch].vpos = 0; } channel[ch].vdepth = param >> 4; channel[ch].vrate = param & 15; break; } } void initresources() { u8 i; struct unpacker up; initup(&up, 0); for(i = 0; i < 16 + MAXTRACK; i++) { resources[i] = readchunk(&up, 13); } initup(&songup, resources[0]); } void playroutine() { // called at 50 Hz u8 ch; u8 lights; if(playsong) { if(trackwait) { trackwait--; } else { trackwait = 4; if(!trackpos) { if(playsong) { if(songpos >= SONGLEN) { //playsong = 0; songpos=0; trackpos=0; initresources(); } else { for(ch = 0; ch < 4; ch++) { u8 gottransp; u8 transp; gottransp = readchunk(&songup, 1); channel[ch].tnum = readchunk(&songup, 6); if(gottransp) { transp = readchunk(&songup, 4); if(transp & 0x8) transp |= 0xf0; } else { transp = 0; } channel[ch].transp = (s8) transp; if(channel[ch].tnum) { initup(&channel[ch].trackup, resources[16 + channel[ch].tnum - 1]); } } songpos++; } } } if(playsong) { for(ch = 0; ch < 4; ch++) { if(channel[ch].tnum) { u8 note, instr, cmd, param; u8 fields; fields = readchunk(&channel[ch].trackup, 3); note = 0; instr = 0; cmd = 0; param = 0; if(fields & 1) note = readchunk(&channel[ch].trackup, 7); if(fields & 2) instr = readchunk(&channel[ch].trackup, 4); if(fields & 4) { cmd = readchunk(&channel[ch].trackup, 4); param = readchunk(&channel[ch].trackup, 8); } if(note) { channel[ch].tnote = note + channel[ch].transp; if(!instr) instr = channel[ch].lastinstr; } if(instr) { if(instr == 2) light[1] = 5; if(instr == 1) { light[0] = 5; if(channel[ch].tnum == 4) { light[0] = light[1] = 3; } } if(instr == 7) { light[0] = light[1] = 30; } channel[ch].lastinstr = instr; channel[ch].inum = instr; channel[ch].iptr = 0; channel[ch].iwait = 0; channel[ch].bend = 0; channel[ch].bendd = 0; channel[ch].volumed = 0; channel[ch].dutyd = 0; channel[ch].vdepth = 0; } if(cmd) runcmd(ch, cmd, param); } } trackpos++; trackpos &= 31; } } } for(ch = 0; ch < 4; ch++) { s16 vol; u16 duty; u16 slur; while(channel[ch].inum && !channel[ch].iwait) { u8 il[2]; readinstr(channel[ch].inum, channel[ch].iptr, il); channel[ch].iptr++; runcmd(ch, il[0], il[1]); } if(channel[ch].iwait) channel[ch].iwait--; if(channel[ch].inertia) { s16 diff; slur = channel[ch].slur; diff = freqtable[channel[ch].inote] - slur; //diff >>= channel[ch].inertia; if(diff > 0) { if(diff > channel[ch].inertia) diff = channel[ch].inertia; } else if(diff < 0) { if(diff < -channel[ch].inertia) diff = -channel[ch].inertia; } slur += diff; channel[ch].slur = slur; } else { slur = freqtable[channel[ch].inote]; } osc[ch].freq = slur + channel[ch].bend + ((channel[ch].vdepth * sinetable[channel[ch].vpos & 63]) >> 2); channel[ch].bend += channel[ch].bendd; vol = osc[ch].volume + channel[ch].volumed; if(vol < 0) vol = 0; if(vol > 255) vol = 255; osc[ch].volume = vol; duty = osc[ch].duty + channel[ch].dutyd; if(duty > 0xe000) duty = 0x2000; if(duty < 0x2000) duty = 0xe000; osc[ch].duty = duty; channel[ch].vpos += channel[ch].vrate; } lights = 0; if(light[0]) { light[0]--; lights |= 0x04; } if(light[1]) { light[1]--; lights |= 0x10; } PORTB = lights; } int main() { asm("cli"); watchdogoff(); CLKPR = 0x80; CLKPR = 0x80; DDRC = 0x12; DDRD = 0xff; //PORTC = 0; pinMode(10,OUTPUT); pinMode(12,OUTPUT); timetoplay = 0; trackwait = 0; trackpos = 0; playsong = 1; songpos = 0; osc[0].volume = 0; channel[0].inum = 0; osc[1].volume = 0; channel[1].inum = 0; osc[2].volume = 0; channel[2].inum = 0; osc[3].volume = 0; channel[3].inum = 0; initresources(); TCCR0A = 0x02; TCCR0B = 0x02; // clkIO/8, so 1/8 MHz OCR0A = 125;//125; // 8 KHz TCCR2A=0b10100011; TCCR2B=0b00000001; TIMSK0 = 0x02; asm("sei"); for(;;) { while(!timetoplay); timetoplay--; playroutine(); } } ISR(TIMER0_COMPA_vect) // called at 8 KHz { u8 i; s16 acc; u8 newbit; OCR2B = lastsample; newbit = 0; if(noiseseed & 0x80000000L) newbit ^= 1; if(noiseseed & 0x01000000L) newbit ^= 1; if(noiseseed & 0x00000040L) newbit ^= 1; if(noiseseed & 0x00000200L) newbit ^= 1; noiseseed = (noiseseed << 1) | newbit; if(callbackwait) { callbackwait--; } else { timetoplay++; callbackwait = 180 - 1; } acc = 0; for(i = 0; i < 4; i++) { s8 value; // [-32,31] switch(osc[i].waveform) { case WF_TRI: if(osc[i].phase < 0x8000) { value = -32 + (osc[i].phase >> 9); } else { value = 31 - ((osc[i].phase - 0x8000) >> 9); } break; case WF_SAW: value = -32 + (osc[i].phase >> 10); break; case WF_PUL: value = (osc[i].phase > osc[i].duty)? -32 : 31; break; case WF_NOI: value = (noiseseed & 63) - 32; break; default: value = 0; break; } osc[i].phase += osc[i].freq; acc += value * osc[i].volume; // rhs = [-8160,7905] } // acc [-32640,31620] lastsample = 128 + (acc >> 8); // [1,251] } }