// Written by Kouzerumatsukite (28-30 Ags 2022)
// The "Tsukite the Bananafox" song is composed by me
// Original: https://www.youtube.com/watch?v=YDCRszWIDwk
// the song in this project is the stripped version of it.
// And the `macro-based` music driver here is written by me,
// which in the design was inspired by Trackers known to me,
// like FamiTracker, VortexTracker, and Monotone Tracker.
// Its simply migration from
// https://johnearnest.github.io/Octo/index.html?key=TloHp4JQ
// (click the site to activate sound, click X to view source)
// Monotonic (square wave only) version:
// https://wokwi.com/projects/341297619980517970
// The playback consistency relies on the audiobuffer
// the buffer length is set to 2048 samples
// means it lags 4 frames behind before buffer runs out,
// This should be good enough for 15625hz samplerate
//BROAD: Use 128X64 screen;
//Comment this if you're using 128x32
#define SCREEN_BROAD 1
//#define DEBUG 1 // monitor the states to serial
//#define MONITOR 1 // monitor the buffer health to serial
#include <avr/pgmspace.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#ifdef SCREEN_BROAD
#define SCREEN_HEIGHT 64
#else
#define SCREEN_HEIGHT 32
#endif
static Adafruit_SSD1306 display(128,SCREEN_HEIGHT,&Wire,-1,800000L,100000L);
#define ____ 0b00000000
#define H___ 0b11000000
#define HH__ 0b11110000
#define HHH_ 0b11111100
#define HHHH 0b11111111
const byte PROGMEM smpData[] { // SAMPLE BUFFER DATA [ 128 bytes ]
____,____,____,____,____,____,____,____,____,____,____,____,____,____,____,____, // 0x00 | Silence
HH__,____,____,____,HH__,____,____,____,HH__,____,____,____,HH__,____,____,____, // 0x10 | 12.5% Pulse Duty
HHHH,____,____,____,HHHH,____,____,____,HHHH,____,____,____,HHHH,____,____,____, // 0x20 | 25.0% Pulse Duty
HHHH,HHHH,____,____,HHHH,HHHH,____,____,HHHH,HHHH,____,____,HHHH,HHHH,____,____, // 0x30 | 50.0% Pulse Duty
0x41,0x47,0xE1,0xE4,0x91,0x16,0xD9,0x9D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x40 | 50.0% Pulse Noise
0x35,0x53,0xAF,0xFA,0xF8,0x87,0x04,0xC4,0x06,0xA6,0x05,0x75,0x87,0xCF,0x44,0xA8, // 0x50 | 100.0% Pulse Noise
0x66,0xFC,0x55,0x02,0x7F,0x83,0xC0,0xC2,0xA0,0xA3,0x70,0xF2,0x48,0x8B,0xEC,0xCE, // 0x60 | 100.0% Pulse Noise
0x9A,0xA9,0x57,0x7D,0xFC,0x43,0x02,0x62,0x03,0xD3,0x82,0xBA,0xC3,0x67,0x22,0x54, // 0x70 | 100.0% Pulse Noise
HHHH,HHH_,____,____,HHHH,HHH_,____,____,HHHH,HHH_,____,____,HHHH,HHH_,____,____, // 0x80 | 43.25% Pulse Duty
HHHH,HH__,____,____,HHHH,HH__,____,____,HHHH,HH__,____,____,HHHH,HH__,____,____, // 0x90 | 37.50% Pulse Duty
HHHH,H___,____,____,HHHH,H___,____,____,HHHH,H___,____,____,HHHH,H___,____,____, // 0xA0 | 31.75% Pulse Duty
HHH_,____,____,____,HHH_,____,____,____,HHH_,____,____,____,HHH_,____,____,____, // 0xB0 | 18.25% Pulse Duty
};
const byte PROGMEM instrSet[] { // SAMPLE AND ORNAMENT SEQUENCE MAPPER [ 128 bytes ]
// 0 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14 15
// __________ __________ __________ __________ __________ __________ __________ __________
//| smpI ornI| smpI ornI| smpI ornI| smpI ornI| smpI ornI| smpI ornI| smpI ornI| smpI ornI|
0x00,0x00, 0x10,0x10, 0x20,0x20, 0x30,0x30, 0x80,0x01, 0x80,0x11, 0xC0,0x01, 0xC0,0x11, // Silence + HKS & 12
0x01,0x00, 0x11,0x10, 0x21,0x20, 0x31,0x30, 0x01,0x21, 0x40,0x21, 0x80,0x21, 0xC0,0x21, // User-def instruments
0x40,0x00, 0x50,0x10, 0x60,0x20, 0x70,0x30, 0x40,0x40, 0x50,0x50, 0x60,0x60, 0x70,0x70, // 12.5% Lead + HKS +
0x40,0x80, 0x50,0x90, 0x60,0xA0, 0x70,0xB0, 0x40,0xC0, 0x50,0xD0, 0x60,0xE0, 0x70,0xF0, // maj-min-dim chords
0x80,0x00, 0x90,0x10, 0xA0,0x20, 0xB0,0x30, 0x80,0x40, 0x90,0x50, 0xA0,0x60, 0xB0,0x70, // 25.0% Lead + HKS +
0x80,0x80, 0x90,0x90, 0xA0,0xA0, 0xB0,0xB0, 0x80,0xC0, 0x90,0xD0, 0xA0,0xE0, 0xB0,0xF0, // maj-min-dim chords
0xC0,0x00, 0xD0,0x10, 0xE0,0x20, 0xF0,0x30, 0xC0,0x40, 0xD0,0x50, 0xE0,0x60, 0xF0,0x70, // 50.0% Lead + HKS +
0xC0,0x80, 0xD0,0x90, 0xE0,0xA0, 0xF0,0xB0, 0xC0,0xC0, 0xD0,0xD0, 0xE0,0xE0, 0xF0,0xF0, // maj-min-dim chords
};
const byte PROGMEM smpRef[] { // SAMPLE SEQUENCE DATA [ 256 bytes ]
// Loop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// PREDEFINED SAMPLE DATA
15, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // [0x00] Silence
15, 0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // [0x10] Hat only
15, 0x50,0x40,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // [0x20] Kick only
15, 0x70,0x60,0x50,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // [0x30] Snare only
15, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // [0x40] 12.5 % Pulse Tone
15, 0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // [0x50] Hat + 12.5 % Pulse
15, 0x50,0x40,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // [0x60] Kick + 12.5 % Pulse
15, 0x70,0x60,0x50,0x40,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // [0x70] Snare + 12.5 % Pulse
15, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, // [0x80] 25 % Pulse Tone
15, 0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, // [0x90] Hat + 25 % Pulse
15, 0x50,0x40,0x30,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, // [0xA0] Kick + 25 % Pulse
15, 0x70,0x60,0x50,0x40,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, // [0xB0] Snare + 25 % Pulse
15, 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, // [0xC0] 50 % Pulse Tone
15, 0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, // [0xD0] Hat + 50 % Pulse
15, 0x50,0x40,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, // [0xE0] Kick + 50 % Pulse
15, 0x70,0x60,0x50,0x40,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30, // [0xF0] Snare + 50 % Pulse
8, 0x20,0xA0,0x90,0x80,0x30,0x80,0x90,0xA0,0x20,0xA0,0x90,0x80,0x30,0x80,0x90, // [0x01] 25%-50% PWM Tone
8, 0x70,0xA0,0x90,0x80,0x30,0x80,0x90,0xA0,0x20,0xA0,0x90,0x80,0x30,0x80,0x90, // [0x11] Hat + 25%-50% PWM
8, 0x50,0x40,0x30,0x80,0x30,0x80,0x90,0xA0,0x20,0xA0,0x90,0x80,0x30,0x80,0x90, // [0x21] Kick + 25%-50% PWM
8, 0x70,0x60,0x50,0x40,0x30,0x80,0x90,0xA0,0x20,0xA0,0x90,0x80,0x30,0x80,0x90, // [0x31] Snare + 25%-50% PWM
// theres no other samples needed beside above, hence nothing.
};
const byte PROGMEM ornData[] { // ORNAMENT SEQUENCE DATA [ 256 bytes ]
// Odd values (e.g. 63 67 71 ) is fixed pitch alter.
// Even values (e.g 4 8 12 -4 -8 -12 ) is relative pitch alter.
// Zero value won't alter the current pitch.
// Loop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// PREDEFINED ORNAMENTS DATA
13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x00] Tone
13, 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x10] Hat + tone
13, 147, 103, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x20] Kick + tone
13, 107, 171, 159, 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x30] Snare + tone
13, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, 0, // [0x40] Major chord tone
13, 211, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, // [0x50] Hat + major chord
13, 147, 103, 67, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, 0, // [0x60] Kick + major chord
13, 107, 171, 159, 147, 16, 28, 0, 16, 28, 0, 16, 28, 0, 16, 28, // [0x70] Snare + major chord
13, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, 0, // [0x80] Minor chord tone
13, 211, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, // [0x90] Hat + minor chord
13, 147, 103, 67, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, 0, // [0xA0] Kick + minor chord
13, 107, 171, 159, 147, 12, 28, 0, 12, 28, 0, 12, 28, 0, 12, 28, // [0xB0] Snare + minor chord
13, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, 0, // [0xC0] Diminish chord tone
13, 211, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, // [0xD0] Hat + diminish chord
13, 147, 103, 67, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, 0, // [0xE0] Kick + diminish chord
13, 107, 171, 159, 147, 12, 24, 0, 12, 24, 0, 12, 24, 0, 12, 24, // [0xF0] Snare + diminish chord
15, -20, -16, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x01] SlideUp
15, 20, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // [0x11] SlideDown
10, 2, 2, 0, -2, -2, 0, 2, 2, 0, -2, -2, 0, 2, 2, 0, // [0x21] Vibratio
};
const PROGMEM byte seqData[] { // the sequence data, song name: Tsukite the Bananafox
0x13,0x80,0x00,0x00,0x43,0x80,0x00,0x00,0x73,0x00,0x00,0x00,0x43,0x80,0x5F,0x10,
0x57,0x88,0x00,0x00,0x57,0x88,0x00,0x00,0x57,0x8C,0x00,0x00,0x57,0x88,0x57,0x8C,
0x00,0x00,0x57,0x8C,0x57,0x88,0x57,0xD0,0x57,0x8C,0x00,0x00,0x53,0x84,0x57,0x80,
0x53,0x88,0x00,0x00,0x53,0x88,0x00,0x00,0x53,0x8C,0x00,0x00,0x53,0x88,0x53,0x8C,
0x00,0x00,0x53,0x8C,0x53,0x88,0x43,0x90,0x53,0x8C,0x00,0x00,0x4B,0x84,0x43,0x80,
0x43,0x88,0x4B,0x80,0x53,0x88,0x57,0x80,0x5F,0x8C,0x00,0x00,0x5F,0x88,0x5F,0x8C,
0x00,0x00,0x5F,0x8C,0x5F,0x88,0x5F,0xD0,0x5F,0x8C,0x00,0x00,0x57,0x84,0x53,0x84,
0x43,0x88,0x00,0x00,0x43,0x88,0x00,0x00,0x43,0x8C,0x43,0x90,0x43,0x88,0x43,0x8C,
0x00,0x00,0x43,0x38,0x00,0x00,0x00,0x00,0x5F,0x8C,0x73,0xD0,0x5F,0x8C,0x5F,0x8C,
0x43,0x88,0x4B,0x80,0x53,0x8C,0x57,0x80,0x5F,0x80,0x73,0xD8,0x57,0x8C,0x53,0x80,
0x43,0x88,0x43,0x38,0x5F,0x8C,0x53,0x88,0x57,0x80,0x73,0xD8,0x53,0x8C,0x4B,0x80,
0x43,0x88,0x43,0x38,0x53,0x8C,0x57,0x80,0x5F,0x80,0x73,0xD8,0x57,0x8C,0x53,0x80,
0x43,0x88,0x43,0x38,0x5F,0x8C,0x67,0x88,0x57,0x80,0x73,0xD8,0x53,0x8C,0x4B,0x8C,
0x43,0x88,0x2F,0x14,0x53,0x8C,0x57,0x80,0x5F,0x80,0x73,0xD8,0x57,0x8C,0x53,0x80,
0x43,0x88,0x43,0x38,0x5F,0x8C,0x53,0x88,0x57,0x80,0x5F,0x88,0x57,0x8C,0x53,0x88,
0x43,0x88,0x43,0x38,0x53,0x8C,0x57,0x80,0x5F,0x80,0x73,0xD8,0x57,0x8C,0x53,0x80,
0x5F,0x88,0x73,0xD0,0x57,0x8C,0x53,0x88,0x5F,0x80,0x73,0xDC,0x67,0x8C,0x5F,0x8C,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x83,0x8D,0x00,0x02,0x87,0x85,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x73,0x59,0x00,0x02,0x87,0x8D,0x37,0xC2,0x83,0x85,0x00,0x02,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x8F,0x8D,0x00,0x02,0x83,0x89,0x57,0xC2,
0x87,0x85,0x87,0x3A,0x73,0x59,0x00,0x02,0x83,0x8D,0x43,0xC2,0x7B,0x85,0x00,0x02,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x83,0x8D,0x00,0x02,0x87,0x85,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x73,0x59,0x00,0x02,0x8F,0xCD,0xA3,0x1A,0xA3,0xC5,0x00,0x02,
0x9F,0xC9,0x5F,0xC2,0x9F,0xC5,0x00,0x02,0xA3,0xCD,0x00,0x02,0x8F,0xC9,0x67,0xC2,
0x8F,0xCD,0x8F,0x3E,0x87,0xC9,0x00,0x02,0x97,0xCD,0x00,0x02,0x8F,0xCD,0x73,0x1E,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x83,0x8D,0x00,0x02,0x87,0x85,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x73,0x59,0x00,0x02,0x87,0x8D,0x37,0xC2,0x83,0x85,0x00,0x02,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x8F,0x8D,0x00,0x02,0x83,0x89,0x57,0xC2,
0x87,0x85,0x00,0x02,0x8F,0x89,0x00,0x02,0x87,0x8D,0x43,0xC2,0x83,0x85,0x00,0x02,
0x73,0x89,0x43,0xC2,0x73,0x85,0x00,0x02,0x83,0x8D,0x00,0x02,0x87,0x85,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x73,0x59,0x00,0x02,0x8F,0x8D,0xA3,0x12,0xA3,0x85,0xA3,0x3A,
0xAB,0x8D,0x00,0x02,0xAB,0x85,0xAB,0x3A,0xA3,0x8D,0x00,0x02,0x9F,0x8D,0x00,0x02,
0x9F,0x85,0x9F,0x3A,0x9F,0x8D,0x8F,0x16,0x8F,0x8D,0x00,0x02,0x8F,0x8D,0x8F,0x3A,
0xA3,0x4C,0x9F,0x80,0xA3,0x48,0x8F,0x4C,0x73,0x90,0x83,0x88,0x87,0x8C,0x87,0x38,
0x00,0x00,0x83,0x8C,0x87,0xC8,0x83,0x88,0x87,0x8C,0x8F,0xC8,0x87,0x8C,0x83,0x8C,
0x87,0x89,0x53,0xC2,0x83,0x85,0x73,0x92,0x87,0x8D,0x00,0x02,0x73,0x89,0x53,0xC2,
0x73,0x85,0x00,0x02,0x73,0x89,0x73,0x3A,0x8F,0x8D,0x00,0x02,0x87,0x85,0x00,0x02,
0x97,0x89,0x57,0xC2,0x8F,0x85,0x87,0xD2,0x8F,0x8D,0x8F,0x3A,0x8F,0x89,0x57,0xC2,
0x87,0x85,0x87,0x82,0x83,0x89,0x83,0x3A,0x87,0x8D,0x00,0x02,0x83,0x85,0x83,0x3A,
0x87,0x89,0x5F,0xC2,0x83,0x85,0x73,0x92,0x87,0x8D,0x00,0x02,0x8F,0x89,0x5F,0xC2,
0x8F,0x85,0x8F,0x3A,0x9F,0x89,0xA3,0x82,0x9F,0x8D,0x00,0x02,0xA3,0x85,0x00,0x02,
0x8F,0x89,0x57,0xC2,0x87,0x85,0x87,0xD2,0x83,0x8D,0x00,0x02,0x87,0x89,0x57,0xC2,
0x8F,0x85,0x00,0x02,0x8F,0x89,0x00,0x02,0x8F,0x8D,0x8F,0x3A,0x8F,0x8D,0x8F,0x3A,
0x87,0x89,0x53,0xC2,0x83,0x85,0x73,0x92,0x87,0x8D,0x00,0x02,0x83,0x89,0x53,0xC2,
0x87,0x85,0x00,0x02,0x83,0x89,0x83,0x3A,0x87,0x8D,0x00,0x02,0x83,0x85,0x83,0x3A,
0x83,0x89,0x57,0xC2,0x7B,0x85,0x87,0xD2,0x83,0x8D,0x00,0x02,0x7B,0x89,0x57,0xC2,
0x83,0x85,0x00,0x02,0x7B,0x89,0x7B,0x3A,0x83,0x8D,0x00,0x02,0x87,0x85,0x00,0x02,
0x8F,0x89,0x5F,0xC2,0x87,0x85,0x8F,0xD2,0x8F,0x8D,0x00,0x02,0x87,0x89,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x87,0x89,0x87,0x3A,0x8F,0x8D,0x00,0x02,0x87,0x85,0x00,0x02,
0x8F,0x89,0x5F,0xC2,0x87,0x85,0x8F,0xD2,0x8F,0x8D,0x00,0x02,0x87,0x89,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x97,0x89,0x00,0x02,0x83,0x8D,0x00,0x02,0x87,0x8D,0x00,0x02,
0x73,0x49,0x53,0xC2,0x6F,0x45,0x73,0x92,0x73,0x4D,0x00,0x02,0x8F,0x49,0x53,0xC2,
0x8F,0x45,0x8F,0x36,0x8F,0x49,0x8F,0x36,0x87,0x4D,0x53,0xC2,0x83,0x45,0x00,0x02,
0x87,0x49,0x57,0xC2,0x8F,0x45,0x87,0xD2,0x8F,0x4D,0x8F,0x36,0x8F,0x49,0x57,0xC2,
0xA3,0x45,0x00,0x02,0x9F,0x49,0x00,0x02,0xA3,0x4D,0x57,0xC2,0x9F,0x45,0x00,0x02,
0xAB,0x49,0x5F,0xC2,0xAB,0x45,0x8F,0xD2,0xA3,0x4D,0xA3,0x36,0x9F,0x49,0x5F,0xC2,
0x8F,0x45,0x00,0x02,0x8F,0x49,0x8F,0x36,0x87,0x4D,0x5F,0xC2,0x87,0x45,0x00,0x02,
0x83,0x49,0x57,0xC2,0x83,0x45,0x87,0x42,0x87,0x0D,0x87,0x42,0x8F,0x49,0x57,0xC2,
0x73,0x45,0x00,0x02,0x73,0x49,0x00,0x02,0x8F,0x4D,0x57,0xC2,0x8F,0x4D,0x8F,0x36,
0x87,0x89,0x57,0xC2,0x83,0x85,0x87,0xD2,0x87,0x8D,0x00,0x02,0x83,0x89,0x57,0xC2,
0x87,0x85,0x00,0x02,0x83,0x89,0x00,0x02,0x87,0x8D,0x57,0xC2,0x83,0x85,0x00,0x02,
0x83,0x89,0x53,0xC2,0x7B,0x85,0x83,0xE2,0x83,0x8D,0x00,0x02,0x7B,0x89,0x53,0xC2,
0x83,0x85,0x00,0x02,0x7B,0x89,0x00,0x02,0x83,0x8D,0x53,0xC2,0x87,0x8D,0x00,0x02,
0x8F,0x89,0x5F,0xC2,0x87,0x85,0x8F,0xD2,0x8F,0x8D,0x00,0x02,0x87,0x89,0x5F,0xC2,
0x8F,0x85,0x00,0x02,0x97,0x89,0x00,0x02,0x8F,0x8D,0x5F,0xC2,0x87,0x85,0x00,0x02,
0x73,0x8D,0x43,0xC2,0x73,0x85,0x73,0xD2,0x73,0x85,0x00,0x02,0x6F,0x8D,0x3F,0xC2,
0x6F,0x85,0x00,0x02,0x6F,0x8D,0x00,0x02,0x6F,0x8D,0x5F,0x16,0x5F,0x8D,0x00,0x02,
};
uint16_t ornPtr = 1; // ornament pointer
uint16_t smpPtr = 1; // sample pointer
uint8_t tickCtr = 0; // tick counter
uint8_t tickFin = 1; // tick final
uint8_t rowCtr = 255; // row counter
uint8_t rowFin = 8; // row final
uint16_t pageCtr = 0; // page counter
uint16_t pageFin = 67; // page fin
uint8_t notekey = 0; // current loaded pitch
uint8_t pitch = 0; // currently playing pitch
uint16_t freq = 0; // translated to freq
const byte PROGMEM seqTime[] { // Song timeline of pages
0 ,
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,
9 , 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64,
65, 66,
};
const byte seqLoop = 9; // The song loop point
const byte PROGMEM seqTicks[] {
// Ticks speed
// _______ _______ _______ _______
// |Speed A|Speed B|Speed C|Speed D|
9, 5, 4, 3,
};
static char* toHex(uint64_t value, uint8_t digits){
static char result[17]; if(digits>16) digits=16;
for(byte k=0; k<digits; k++)
result[k] = "0123456789ABCDEF"[value>>(digits-1-k)*4&15];
result[digits] = 0;
return result;
}
static char* toPatt(uint16_t value){
static char result[8];
uint8_t note = ( value >> 8 ) - 3 >> 2;
uint8_t macr = ( value & 0xFF ) >> 2;
if((value >> 8)==0){
result[0] = '.';
result[1] = '.';
result[2] = '.';
}else{
if(value&0xF0){
result[0] = "GAABCCDDEFFG"[note%12];
result[1] = "#-#--#-#--#-"[note%12];
result[2] = (note-4)/12+0x31;
}else{
result[0] = '-';
result[1] = '-';
result[2] = '-';
}
}
result[3] = 32;
result[4] = "-UUUAAAABBBBCCCC"[macr/4];
result[5] = ".---.JID.JID.JID"[macr/4];
result[6] = macr<16?".HKS0123456789AB"[macr]:".HDS"[macr&3];
result[7] = 0;
return result;
}
void setupTimers(){
// Modified in slight bit for suitable need, from reference:
// https://makezine.com/projects/advanced-arduino-sound-synthesis/#:~:text=Just%20connect%20a%20digital%20output,and%20ground%20without%20any%20amplification.
/******** Set up timer2 to call ISR ********/
TCCR2A = 0;
TCCR2B = 0;
TCCR2B |= 0b011; // 3-bit of prescaler
TIMSK2 = (1 << OCIE2A); // Call ISR when TCNT2 = OCRA2
OCR2A = 32; // Set frequency of generated wave
sei(); // Enable interrupts to generate waveform!
}
/******** Lookup table ********/
static byte* bufferData=NULL; // Storage for waveform
static uint16_t bufferPosA=0;
static uint16_t bufferPosB=0;
static uint16_t bufferNeed=0;
static uint8_t buffPtr;
#ifdef SCREEN_BROAD
#define AUDIO_BUFFER_LENGTH 0x1FF
#else
#define AUDIO_BUFFER_LENGTH 0xFF
#endif
uint8_t getBufferByte(uint16_t addr){
addr &= AUDIO_BUFFER_LENGTH;
//addr = (addr&7)<<7 | (addr>>3&63);
addr = addr&63 | addr>>6<<7;
return bufferData[addr];
};
void setBufferByte(uint16_t addr, uint8_t val){
addr &= AUDIO_BUFFER_LENGTH;
//addr = (addr&7)<<7 | (addr>>3&63);
addr = addr&63 | addr>>6<<7;
bufferData[addr] = val;
};
static uint16_t sampPos=0;
static bool lastSamp;
bool getSamp(){ sampPos += freq;
lastSamp = pgm_read_byte(smpData+buffPtr+(sampPos>>12&15))>>7-(sampPos>>9&7)&1;
return lastSamp;
};
static bool refreshDisp = false;
static byte refreshTick = 0;
void performTick(){ refreshTick--;
buffPtr = pgm_read_byte(smpRef+smpPtr); // update buffer
uint8_t orn = pgm_read_byte(ornData+ornPtr);
pitch = orn&1?orn:notekey+(int8_t)orn;
freq = (uint16_t)(128*pow(2,(double)(pitch-64.)/48.)*OCR2A/32);
if(!digitalRead(2)){
uint16_t l = 256/OCR2A*32;
for(byte k = 0; k < l/8; k++){
if(bufferPosA - bufferPosB + 8 > AUDIO_BUFFER_LENGTH*8){
break;
}
byte val = getSamp();
val += val + getSamp();
val += val + getSamp();
val += val + getSamp();
val += val + getSamp();
val += val + getSamp();
val += val + getSamp();
val += val + getSamp();
setBufferByte(bufferPosA>>3,val);
bufferPosA += 8;
}
}
if(!(++ornPtr&15)){ornPtr -= 16; ornPtr += pgm_read_byte(ornData+ornPtr);}
if(!(++smpPtr&15)){smpPtr -= 16; smpPtr += pgm_read_byte(smpRef+smpPtr);}
if(++tickCtr==tickFin){ tickCtr = 0; // update row
if(++rowCtr==rowFin){ rowCtr = 0; // update page
if(++pageCtr==pageFin) pageCtr = seqLoop; // loop
}
auto idx = seqData+rowCtr*2+16*pgm_read_byte(seqTime+pageCtr);
uint16_t seq = (pgm_read_byte(idx+0)<<8)+pgm_read_byte(idx+1);;
tickFin = pgm_read_byte(seqTicks+(seq&3)); // extract tickspeed
if(seq >> 8){
notekey = seq >> 8; // extract pitch
auto insidx = instrSet+(seq>>2&0b111111)*2;
uint16_t ins = (pgm_read_byte(insidx)<<8)+pgm_read_byte(insidx+1); // extract instrument
smpPtr = ins>>8&0xF0 | (ins>>8&0xF)<<8 | 1; // sample pointer
ornPtr = ins>>0&0xF0 | (ins>>0&0xF)<<8 | 1; // orn pointer
}
refreshDisp = true;
}
// print buffer health
// vvbvvvvvvvvvvvvvvvvvvvvv
//Serial.println(bufferNeed);
// ^^^^^^^^^^^^^^^^^^^^^^^^
// print buffer health
}
void performDisp(){
refreshDisp = false;
display.setTextSize(1);
#ifndef SCREEN_BROAD
#define TRK_LEN 4
byte o = rowCtr&0b100;
#else
#define TRK_LEN 8
byte o = 0;
#endif
auto idx = seqData+16*pgm_read_byte(seqTime+pageCtr+o);
for(byte j = o; j < TRK_LEN+o; j++){
//display.setCursor((j>>2)*64,(j&3)*8);
display.setCursor(0,(j-o)*8);
display.setTextColor(j!=rowCtr,j==rowCtr);
uint16_t seq = (pgm_read_byte(idx+0+j*2)<<8)+pgm_read_byte(idx+1+j*2);
display.print(j); display.print(" ");
display.print(toPatt(seq));
display.print(seq&3);
}
display.display();
}
#include <avr/interrupt.h> // Use timer interrupt library
static uint16_t divVal = 256;
static uint16_t divCnt = divVal;
static uint16_t sampleCount=0;
static double sampleSpeed=0;
/******** Called every time TCNT2 = OCR2A ********/
ISR(TIMER2_COMPA_vect) { // Called when TCNT2 == OCR2A
TCNT2 -= OCR2A;
if(bufferPosA - bufferPosB){
PORTB = 0-(getBufferByte(bufferPosB>>3)>>7-(bufferPosB&7)&1);
if((bufferPosB&0b111)==7)setBufferByte(bufferPosB>>3,0);
bufferPosB++;
}
if(!--divCnt){
divCnt += divVal;
refreshTick++;
}
sampleCount++;
}
void setup() {
Serial.begin(9600);
pinMode(2,INPUT);
pinMode(13,OUTPUT);
digitalWrite(0,LOW);
digitalWrite(1,LOW);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
//display.ssd1306_command(0xDA);
//display.ssd1306_command(0x10);
display.display();
display.clearDisplay();
bufferData = display.getBuffer()+64;
// put your setup code here, to run once:
setupTimers();
divVal = 256/OCR2A*32;
divCnt = divVal;
}
uint32_t lastMillis=millis();
void fillzero(int32_t value){
if(value<1000){
Serial.print(' ');
if(value<100){
Serial.print(' ');
if(value<10){
Serial.print(' ');
}
}
}
}
void loop() {
uint16_t bufferLast = bufferPosA-bufferPosB;
while(refreshTick) performTick();
uint16_t bufferLeft = bufferPosA-bufferPosB;
#ifdef MONITOR
fillzero(bufferLast); Serial.print(bufferLast); Serial.print(", ");
fillzero(bufferLeft); Serial.print(bufferLeft); Serial.println();
#endif
//if(refreshDisp) // update screen only when the row updates
performDisp();
#ifdef DEBUG
Serial.print("PRT: ");
Serial.print(toHex(pageCtr,2));
Serial.print(toHex(rowCtr,2));
Serial.print(toHex(tickCtr,2));
Serial.print(", PNO: ");
Serial.print(toHex(pitch,2));
Serial.print(toHex(notekey,2));
Serial.print(toHex(orn,2));
Serial.print(", Ptrs: ");
Serial.print(toHex(ornPtr,3));
Serial.print(toHex(smpPtr,3));
Serial.println();
#endif
/* //monitor the samplerate
uint32_t currMillis = millis();
if(currMillis - lastMillis >= 1000){
double val = (double)(currMillis-lastMillis)/1000.;
val = sampleCount / val; sampleCount = 0;
if(sampleSpeed==0.) sampleSpeed = val;
else sampleSpeed += (val-sampleSpeed)/4;
Serial.println(sampleSpeed);
lastMillis = millis();
}
*/
}