// #define UNIT_TEST_MODE 1
// #define TEST_UNIT_TESTS 1
// #define DEBUG 1
#define CUSTOM_FONT 1
#define ALIGNMENT 1
// #define SHITTY_DISPLAY 1
#include "Arduino.h"
// #include "MockArduino.h"
const int RXLED = 17;
#include "Common.h"
const char compile_date[] = __DATE__ " " __TIME__;
long lastUpdate = millis();
long thisUpdate = lastUpdate;
long displayDelay = 100; // 200 ms delay for display updates
// bool doUpdate = false;
const int MIDDLE_C = 60;
const int channel = 1;
/**
#ifdef UNIT_TEST_MODE
#include "MockMidi.h"
#ifdef CUSTOM_FONT
#include "MockFont.h"
#endif // CUSTOM_FONT
#include "MockDisplay.h"
#include "Tests.h"
#else // ! UNIT_TEST_MODE
#include <USB-MIDI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#ifdef SHITTY_DISPLAY
#include <Adafruit_SH110X.h>
#else
#include <Adafruit_SSD1306.h>
#endif
#include <BasicEncoder.h>
#ifdef CUSTOM_FONT
// #include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#define _FONTNAME_ FreeSans12pt7b
#endif // CUSTOM_FONT
#endif // UNIT_TEST_MODE
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "MockMidi.h"
// #include "MockFont.h"
// #include "Tests.h"
// #include <BasicEncoder.h>
#include "MockEncoder.h"
#define _FONTNAME_ FreeSans12pt7b
#include <Fonts/FreeSans12pt7b.h>
USING_NAMESPACE_MIDI;
USBMIDI_CREATE_INSTANCE(0, MIDI);
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#ifdef SHITTY_DISPLAY
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define DISPLAY_WHITE SH110X_WHITE
#else
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define DISPLAY_WHITE SSD1306_WHITE
#endif
// TODO: Refactor to decouple from display
#include "Controllers.h"
#include "NoteState.h"
#include "NotePlayer.h"
const int8_t rotaryEncoderPinA = A0;
const int8_t rotaryEncoderPinB = A1;
BasicEncoder rotaryEncoder(rotaryEncoderPinA, rotaryEncoderPinB, HIGH, 2);
void InterruptSetup(byte pin) // Setup pin change interupt on pin
{
*digitalPinToPCMSK(pin) |= bit(digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit(digitalPinToPCICRbit(pin)); // clear outstanding interrupt
PCICR |= bit(digitalPinToPCICRbit(pin)); // enable interrupt for group
}
void SetupEncoder(int pinA, int pinB) {
uint8_t old_sreg = SREG; // save the current interrupt enable flag
noInterrupts();
InterruptSetup(pinA);
InterruptSetup(pinB);
rotaryEncoder.reset();
SREG = old_sreg; // restore the previous interrupt enable flag state
}
ISR(PCINT1_vect) // pin change interrupt for A0+
{
DEBUG_PRINTLN("INTERRUPT!");
rotaryEncoder.service();
}
void OnChangeQueuedNotes();
NoteState noteState(MIDDLE_C, &OnChangeQueuedNotes, unisonChord);
NotePlayer notePlayer(noteState, &MIDI, channel);
void OnChangeQueuedNotes()
{
DEBUG_PRINT("Selected: ");
DEBUG_PRINT(noteState.GetName());
DEBUG_PRINT(noteState.GetChord()->GetChordName());
}
void IncrementPitch(FootswitchState state)
{
if (state == Down)
noteState.Inc();
}
void DecrementPitch(FootswitchState state)
{
if (state == Down)
noteState.Dec();
}
void PlayPause(FootswitchState state)
{
if (state == Down)
{
if (notePlayer.GetState() == Stopped)
notePlayer.Play();
else
notePlayer.Stop();
}
}
void ResetNoteValue(FootswitchState state)
{
if (state == Down)
{
noteState.ResetNoteValue();
}
}
IController* controllers[] =
{
new Footswitch2(PIND4, &IncrementPitch),
new Footswitch2(PIND5, &DecrementPitch),
new Footswitch2(PIND6, &PlayPause),
new Footswitch2(7, &ResetNoteValue)
};
void DisplayAbout()
{
const char productName[] = "DR ONE";
const char version[] = "Version 0.1";
// Clear the buffer
int16_t x,y;
uint16_t w, h, height2x, height1x;
display.clearDisplay();
#ifdef CUSTOM_FONT
display.setFont(&_FONTNAME_);
#endif
display.setTextColor(DISPLAY_WHITE); // Draw white text
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.setTextWrap(false);
display.setTextSize(1);
display.getTextBounds(productName,0, 0, &x, &y, &w, &height2x);
height2x += 2;
#ifdef CUSTOM_FONT
display.setCursor((SCREEN_WIDTH - w) / 2, height2x); // Start at top-left corner
#else
display.setCursor((SCREEN_WIDTH - w) / 2, 0); // Start at top-left corner
#endif
display.write(productName);
display.drawLine(0, height2x + 3, SCREEN_WIDTH, height2x + 3, DISPLAY_WHITE);
display.setTextSize(1); // Normal 1:1 pixel scale
display.setFont();
display.getTextBounds(version, 0, height2x + 2, &x, &y, &w, &height1x);
display.setCursor(0, SCREEN_HEIGHT - height1x);
display.write(compile_date);
DEBUG_PRINTLN(compile_date);
display.display();
}
void setup() {
Serial.begin(9600);
#ifdef UNIT_TEST_MODE
while(!Serial){ }
#endif // UNIT_TEST_MODE || DEBUG
DEBUG_PRINTLN("Setup start");
MIDI.begin(1);
pinMode(RXLED, OUTPUT);
digitalWrite(RXLED, HIGH);
MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
DEBUG_PRINTLN("Note on");
digitalWrite(RXLED, LOW);
});
MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
DEBUG_PRINTLN("Note off");
digitalWrite(RXLED, HIGH);
});
for (int i = 0; i < countof(controllers); i++)
{
IController* controller = controllers[i];
controller->setup();
}
#ifdef SHITTY_DISPLAY
if(!display.begin(SCREEN_ADDRESS, true)) {
#else
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
#endif
DEBUG_PRINTLN("SSD1306 allocation failed");
for(;;); // Don't proceed, loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
DisplayAbout();
#ifndef UNIT_TEST_MODE
delay(4000); // Pause for 5 seconds
#endif // UNIT_TEST_MODE
display.clearDisplay();
display.display();
}
bool fLedOn = false;
long lastLedTime = millis();
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void loop() {
// defined for unit test mode
MIDI.read();
#ifdef UNIT_TEST_MODE
RunTests();
#endif
bool doUpdate = false;
thisUpdate = millis();
if (thisUpdate > lastUpdate + displayDelay)
{
doUpdate = true;
lastUpdate = thisUpdate;
}
rotaryEncoder.service();
int encoder_change = rotaryEncoder.get_change();
if (encoder_change) {
// noteState.Inc(encoder_change);
noteState.IncChord(encoder_change);
rotaryEncoder.reset();
}
for (int i = 0; i < countof(controllers); i++) {
IController* controller = controllers[i];
controller->updateState();
}
/// THIS VERSION IS WORKING!!!
if (doUpdate)
{
display.clearDisplay();
// DEBUG_PRINTLN("do update.");
int16_t x,y;
uint16_t w, h;
// DRAW PLAY STATE ICON
// const int size = SCREEN_HEIGHT / 2;
const int widthHeight = SCREEN_HEIGHT / 2;
// const int xTop = (SCREEN_WIDTH / 2) - (widthHeight / 2); // centered to the screen
const int xTop = (SCREEN_WIDTH / 3) + ((SCREEN_WIDTH / 3 ) - (widthHeight / 2)); // centered to the second 2/3rds of the screen
const int yTop = (SCREEN_HEIGHT - widthHeight) / 2;
if (notePlayer.GetState() == Playing)
{
// draw triangle
const int x2 = xTop, y2 = yTop + widthHeight;
const int x3 = xTop + widthHeight, y3 = yTop + (widthHeight / 2);
display.fillTriangle(xTop, yTop, x2, y2, x3, y3, DISPLAY_WHITE);
}
else
{
// draw square
display.fillRect(xTop, yTop, widthHeight, widthHeight, DISPLAY_WHITE);
}
display.setTextSize(1);
display.setFont(&_FONTNAME_);
display.setTextSize(1);
h = SCREEN_HEIGHT /2;
display.setCursor(0, h-1); // Start at top-right corner
display.print(noteState.GetName());
// display.println(Chords[0].GetChordName());
/*
Chord* chord = noteState.GetChord();
const char* chordName = chord->GetChordName();
display.println(chordName);
*/
// DRAW OCTAVE
display.setCursor(0, SCREEN_HEIGHT - 1); // Start at top-right corner
display.println(noteState.GetOctave());
display.setFont();
if (notePlayer.GetState() == Playing)
{
const char * playingNote = notePlayer.GetPlayingNoteName();
display.setCursor(SCREEN_WIDTH - 30, SCREEN_HEIGHT - 8); // Start at top-right corner
display.print(playingNote);
// display.println(Chords[0].GetChordName());
/*
Chord* chord = noteState.GetChord();
const char* chordName = chord->GetChordName();
display.println(chordName);
DEBUG_PRINTLN(chordName);
*/
}
display.display();
}
}