// SSL VIRTUAL TAPE MACHINE
// v3.2 Revised PCB
// 23-Apr 2020
// By Anthony Cornish [horizonsound on sslmixed.com]

// Working Transport Control
// First cut with Timecode
// Cleaned up. Crap removed. No Demo.


#include <stdio.h>    // sprintf belongs to this lib

enum { F24 = 0, F25 = 2, F30DF = 4, F30 = 6 }; // Frames type

byte h, m, s, f;      // hour, minutes, secondes, frames time code
byte frameType;       // speed of MTC : 24fps / 25fps / 30 drop frame / 30 fps
byte buf[64];         // Serial Midi Buffer
byte tc[20];          // array to old timecode

const int Rwind = A1;    // RW Switch
const int Fwind = A2;    // FW Switch  
const int Sstop = A3;    // Stop Switch
const int Play = A4;     // Play Switch
const int Rec = A5;      // Rec Switch
const int senRwind = 8;  // RW Lamp
const int senFwind = 7;  // FW Lamp
const int senStop = 6;   // Stop Lamp
const int senPlay = 5;   // Play Lamp
const int senRec = 4;    // Rec Lamp
const int Tach = 3;     // Tach Output
const int Dir = 2;      // Dir Output
const int DutyPlayTach = 15;
const int DutyFastTach = 1;
const int LCDrs = A0;    // LCD RS
const int LCDen = 9;    // LCD EN
const int LCDD4 = 10;     // LCD D4
const int LCDD5 = 11;     // LCD D5
const int LCDD6 = 12;     // LCD D6
const int LCDD7 = 13;    // LCD D7


int buttonStateRwind = 0;
int buttonStateFwind = 0;
int buttonStateStop = 0;
int buttonStatePlay = 0;
int buttonStateRec = 0;
int loopCount = 0;

int buttonLatchRwind = 0;
int buttonLatchFwind = 0;
int buttonLatchStop = 1;
int buttonLatchPlay = 0;
int buttonLatchRec = 0;

#include <LiquidCrystal.h>
LiquidCrystal lcd(A0, 9, 10, 11, 12, 13);
#include <MIDI.h>
#include "Controller.h"

MIDI_CREATE_DEFAULT_INSTANCE();

byte NUMBER_BUTTONS = 5;

//***DEFINE DIRECTLY CONNECTED BUTTONS*******************************
//Button (Pin Number, Command, Note Number, Channel, Debounce Time)
//** Command parameter 0=NOTE  1=CC  2=Toggle CC **

Button BU1(A4, 1, 110, 1, 25 ); //Play
Button BU2(A3, 1, 111, 1, 5 ); //Stop
Button BU3(A5, 1, 112, 1, 5 ); //Record
Button BU4(A1, 1, 113, 1, 5 ); //Rwind
Button BU5(A2, 1, 114, 1, 5 ); //Fwind

Button *BUTTONS[] {&BU1, &BU2, &BU3, &BU4, &BU5};

void setup() {

  // Setup Code:

  Serial.begin(31250);
  MIDI.begin(MIDI_CHANNEL_OFF);
  pinMode(Rwind, INPUT);
  pinMode(Fwind, INPUT);
  pinMode(Sstop, INPUT);
  pinMode(Play, INPUT);
  pinMode(Rec, INPUT);
  pinMode(senRwind, OUTPUT);
  pinMode(senFwind, OUTPUT);
  pinMode(senStop, OUTPUT);
  pinMode(senPlay, OUTPUT);
  pinMode(senRec, OUTPUT);
  pinMode(Tach, OUTPUT);
  pinMode(Dir, OUTPUT);
  pinMode(LCDrs, OUTPUT);
  pinMode(LCDen, OUTPUT);
  pinMode(LCDD4, OUTPUT);
  pinMode(LCDD5, OUTPUT);
  pinMode(LCDD6, OUTPUT);
  pinMode(LCDD7, OUTPUT);
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("SSL V-TAPE v3.2");
  delay(2000);
  lcd.clear();
  delay(1000);  
  lcd.setCursor(0,0);
  lcd.print("STOP  ");
  lcd.setCursor(0,1);
  lcd.print("00:00:00.00");
  lcd.setCursor(14,0);
  lcd.print("25");
}

void loop() {

  // Main Code:

  buttonStateRwind = digitalRead(Rwind);
  buttonStateFwind = digitalRead(Fwind);
  buttonStateStop = digitalRead(Sstop);
  buttonStatePlay = digitalRead(Play);
  buttonStateRec = digitalRead(Rec);

  
if (buttonStateRwind == HIGH) {
    buttonLatchRwind = 1;
    buttonLatchFwind = 0;
    buttonLatchStop = 0;
    buttonLatchPlay = 0;
    buttonLatchRec = 0;
    digitalWrite(senRwind, HIGH);
    digitalWrite(senFwind, LOW);
    digitalWrite(senStop, LOW);
    digitalWrite(senPlay, LOW);
    digitalWrite(senRec, LOW);
    lcd.setCursor(0,0);
    lcd.print("<<    ");
  }
    
if (buttonStateFwind == HIGH) {
    buttonLatchRwind = 0;
    buttonLatchFwind = 1;
    buttonLatchStop = 0;
    buttonLatchPlay = 0;
    buttonLatchRec = 0;    
    digitalWrite(senRwind, LOW);
    digitalWrite(senFwind, HIGH);
    digitalWrite(senStop, LOW);
    digitalWrite(senPlay, LOW);
    digitalWrite(senRec, LOW);
    lcd.setCursor(0,0);
    lcd.print(">>    ");
  }
  
if (buttonStateStop == HIGH) {
    buttonLatchRwind = 0;
    buttonLatchFwind = 0;
    buttonLatchStop = 1;
    buttonLatchPlay = 0;
    buttonLatchRec = 0;    
    digitalWrite(senRwind, LOW);
    digitalWrite(senFwind, LOW);
    digitalWrite(senStop, HIGH);
    digitalWrite(senPlay, LOW);
    digitalWrite(senRec, LOW);
    lcd.setCursor(0,0);
    lcd.print("STOP  ");
  }

  if (buttonStatePlay == HIGH) {
    buttonLatchRwind = 0;
    buttonLatchFwind = 0;
    buttonLatchStop = 0;
    buttonLatchPlay = 1;
    buttonLatchRec = 0;    
    digitalWrite(senRwind, LOW);
    digitalWrite(senFwind, LOW);
    digitalWrite(senStop, LOW);
    digitalWrite(senPlay, HIGH);
    digitalWrite(senRec, LOW);
    lcd.setCursor(0,0);
    lcd.print("PLAY  ");
  }

if (buttonStateRec == HIGH) {
    buttonLatchRwind = 0;
    buttonLatchFwind = 0;
    buttonLatchStop = 0;
    buttonLatchPlay = 1;
    buttonLatchRec = 1;    
    digitalWrite(senRwind, LOW);
    digitalWrite(senFwind, LOW);
    digitalWrite(senStop, LOW);
    digitalWrite(senPlay, HIGH);
    digitalWrite(senRec, HIGH);
    lcd.setCursor(0,0);
    lcd.print("RECORD");
  }
  
if (buttonLatchStop == 1) {
      digitalWrite(Dir, LOW);
      digitalWrite(senStop, HIGH);
}

if (buttonLatchRwind == 1) {
      digitalWrite(Dir, HIGH);
      digitalWrite(Tach, HIGH);
      delay(2);
      digitalWrite(Tach, LOW);
      delay(2);
  }
  
if (buttonLatchFwind == 1) {
      digitalWrite(Dir, LOW);
      digitalWrite(Tach, HIGH);
      delay(2);
      digitalWrite(Tach, LOW);
      delay(2);
  }
  
if (buttonLatchPlay == 1) {
      digitalWrite(Dir, LOW);
      digitalWrite(Tach, HIGH);
      delay(20);
      digitalWrite(Tach, LOW);
      delay(20);  

  int bytestoRead;
  bytestoRead = Serial.available();

  if (bytestoRead>0)
  {
    for (int i=0; i<bytestoRead; i++)
    {
      buf[i]=Serial.read();

      if (buf[i]!=0xF1)
      {
        int indice;

        indice = (buf[i] & 0xf0)>>4;

        if (indice>7)
          continue;

        tc[indice]= buf[i] & 0x0f;

        if (indice==7)
        {
          char toDisp[30];
          char typeStr[10];
          byte frameType;
         
          frameType = tc[7] & 0x06;

          h = (tc[7] & 0x01)*16 + tc[6];
          m = tc[5]*16+tc[4];
          s = tc[3]*16+tc[2];
          f = tc[1]*16+tc[0];

          if (h>23)  h = 23;
          if (h==16) h = 0;
          if (m>59)  m = 59;
          if (s>59)  s = 59;
          if (f>30)  f = 30; 

          switch(frameType)
          {
            case F24:
              strcpy(typeStr, "24 "); break;
            case F25:
              strcpy(typeStr, "25 "); break;
            case F30DF:
              strcpy(typeStr, "30d"); break;
            case F30:
              strcpy(typeStr, "30 "); break;  
            default:
              strcpy(typeStr, "ERR"); break;
          }

          sprintf(toDisp, "%02d:%02d:%02d.%02d", h, m, s, f);
          lcd.setCursor(0,1);
          lcd.print(toDisp);
          lcd.setCursor(14,0);
          lcd.print(typeStr);
        }
      }  
    }
  }
} 

  if (NUMBER_BUTTONS != 0) updateButtons();
  
} // End of main loop


void updateButtons() {

  // Cycle through Button array
  for (int i = 0; i < NUMBER_BUTTONS; i = i + 1) {
    byte message = BUTTONS[i]->getValue();

    //  Button is pressed
    if (message == 0) {
      switch (BUTTONS[i]->Bcommand) {
        case 0: //Note
          MIDI.sendNoteOn(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
          break;
        case 1: //CC
          MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
          break;
        case 2: //Toggle
          if (BUTTONS[i]->Btoggle == 0) {
            MIDI.sendControlChange(BUTTONS[i]->Bvalue, 127, BUTTONS[i]->Bchannel);
            BUTTONS[i]->Btoggle = 1;
          }
          else if (BUTTONS[i]->Btoggle == 1) {
            MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
            BUTTONS[i]->Btoggle = 0;
          }
          break;
      }
    }

    //  Button is not pressed
    if (message == 1) {
      switch (BUTTONS[i]->Bcommand) {
        case 0:
          MIDI.sendNoteOff(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
          break;
        case 1:
          MIDI.sendControlChange(BUTTONS[i]->Bvalue, 0, BUTTONS[i]->Bchannel);
          break;
      }
    }
  }
}
