/*
 * electronic_piano_tasks.hpp
 *
 *  Created on: Nov 5, 2015
 *      Author: Jason
 */

#ifndef ELECTRONIC_PIANO_TASKS_HPP_
#define ELECTRONIC_PIANO_TASKS_HPP_

#include "scheduler_task.hpp"
#include "FreeRTOS.h"
#include "My_spi.hpp"
#include "My_gpio.hpp"
#include "io.hpp"

/* === Modifiable Settings ========================================================================================= */
#define KEY_COUNT               12      // The number of keys
#define ADC_COUNT               6       // The number of ADC peripherals
/* For MIDI_TASK */
#define PAD_FIRST               0       // Byte padding order for SPI RT MIDI MODE
#define BYTE_PADDING            0xFF    // Using VS1033D; requires 0xFF padding for each midi msg field
#define DEFAULT_NOTE_BASE       0x3C    // Default note base (Refer to MIDI note to hexadecimal mapping)
#define DEFAULT_NOTE_SCOPE_INDEX   4    // Represents integer index of note scope; dependent on default note base
/* For IR_KEY_TASK */
#define IR_SAMPLE_COUNT         3       // Target total sample count for valid activation of IR key
#define IR_SAMPLE_RATE_MS       10      // Sample rate of IR sensors / [ms]
enum ir_sensor_sensitivity {            // IR sensitivity setting (May require modification for IR sensor calibration)
    NOTE_ON_LOWER_LIM =         1300,   // IR sensor: closer proximity -> larger value (Used to activate a note)
    NOTE_OFF_UPPER_LIM =        650     // IR sensor: further proximity -> smaller value (Used to deactivate a note)
};
/* For CONTROL_TASK */
#define INPUT_SAMPLE_RATE_MS    20      // Sample rate of on-board push button / [ms]

/* === Constants =================================================================================================== */
#define NOTE_QUEUE_SIZE         12      // FreeRTOS: note queue size
#define COMMAND_QUEUE_SIZE      12      // FreeRTOS: command queue size
#define MAX_VELOCITY            0x7F    // Max velocity for NOTE ON command
#define NOT_USED                0
#define INCREMENT               1       // Parameter for function
#define DECREMENT               0       // Parameter for function
#define MAX_NOTE_BASE           0x60    // Maximum note base, determined by MIDI note to hex chart
#define MIN_NOTE_BASE           0x18    // Minimum note base, determined by MIDI note to hex chart
enum midi_task_cmd {
    NOTE_ON_BASE =              0x90,   // MIDI Standard: The base status value of Note On (channel 1)
    NOTE_OFF_BASE =             0x80,   // MIDI Standard: The base status value of Note Off (channel 1)
    MIDI_CON_BASE =             0xB0,   // MIDI Standard: The base status value of Control Change
    MIDI_CON_VOL =              0x07,   // MIDI Standard: Control Change - control volume
    MIDI_INSTRUMENT_CON =       0xC0,   // MIDI Standard: The base status value of Change Program (channel 1)
    INC_NOTE_SCOPE =            0x01,   // Decrement scope of notes
    DEC_NOTE_SCOPE =            0x00,   // Increment scope of notes
    VOL_UP =                    0x03,   // Increase volume using MIDI volume control
    VOL_DOWN =                  0x02    // Decrease volume using MIDI volume control
};
enum adc_cmd {
    READ_CH0 =                  0xC0,   // Read ADC channel 0
    READ_CH1 =                  0xE0    // Read ADC channel 1
};
enum shared_queue_id {
    COMMAND_QUEUE_ID,
    NOTE_QUEUE_ID,
    VELOCITY_QUEUE_ID,
    CONTROL_QUEUE_ID
};
enum cs_list {                          // Chip select list of available SPI devices
    MIDI_RECEIVER_PORT = 1, MIDI_RECEIVER_PIN = 30,
    ADC_0_PORT = 1, ADC_0_PIN = 29,
    ADC_1_PORT = 1, ADC_1_PIN = 28,
    ADC_2_PORT = 1, ADC_2_PIN = 23,
    ADC_3_PORT = 1, ADC_3_PIN = 22,
    ADC_4_PORT = 1, ADC_4_PIN = 20,
    ADC_5_PORT = 1, ADC_5_PIN = 19,
};
enum key_note_mapping {                 // Key to note mapping; offset from note base
    KEY_0 = 0,
    KEY_1 = 2,
    KEY_2 = 4,
    KEY_3 = 5,
    KEY_4 = 7,
    KEY_5 = 9,
    KEY_6 = 10,
    KEY_7 = 11,
    KEY_8 = 6,
    KEY_9 = 8,
    KEY_10 = 1,
    KEY_11 = 3,
};
enum led_shift_reg {
    UPDATE_LED_PORT = 2, UPDATE_LED_PIN = 4,
};
enum volume {                           // Volume levels (25% steps)
    VOL_MAX =                   0x7F,   // Index: 4
    VOL_HIGH =                  0x60,   // Index: 3
    VOL_MID =                   0x40,   // Index: 2
    VOL_LOW =                   0x20,   // Index: 1
    VOL_OFF =                   0x00,   // Index: 0
    VOL_MAX_INDEX =             4,
    VOL_MIN_INDEX =             0,
};
enum control_task_input {
    NOTE_UP_PORT =      1, NOTE_UP_PIN =    10,
    NOTE_DOWN_PORT =    1, NOTE_DOWN_PIN =  9,
    VOL_UP_PORT =       1, VOL_UP_PIN =     15,
    VOL_DOWN_PORT =     1, VOL_DOWN_PIN =   14
};
typedef char byte_t;

/* === Tasks ======================================================================================================= */

class Midi_task : public scheduler_task
{
    private:
        struct midi_msg_t {             // Struct midi_msg_t represents a MIDI message
            byte_t status;				// Status field
            byte_t data_1;				// Data1 field
            byte_t data_2;				// Data2 field
        };
        byte_t note_base;               // Base reference value of note to be played
        char note_scope_index;          // Current index of scope of notes
        uint8_t current_vol_index;      // Current volume index
        inline void build_midi_msg( struct midi_msg_t *midi_msg, byte_t command, byte_t field_1, byte_t field_2 );
        void send_midi_msg( struct midi_msg_t *midi_msg );	// Send MIDI msg via SPI
        void change_note_base( uint8_t direction );			// Increment/decrement note range
        bool change_vol( uint8_t direction );				// Increment/decrement volume
        void change_vol_led( uint8_t level );				// Change volume represented onboard LEDs
    public:
        Midi_task( uint8_t priority ) : scheduler_task( "midi_task", ( 512 * 2 ), priority ) { }
        bool init ( void );
        bool run( void *p );
};

class Ir_key_task : public scheduler_task
{
    private:
        struct key_t {                  // Struct key_t represents a physical key
            uint8_t id;                 // Each key is associated with a unique identifier
            uint16_t velocity;          // Represents velocity of key press
            uint8_t count;              // Valid activation count for IR debouncing
            uint8_t note;               // Each key is mapped to a note in the form of an offset
            bool active;                // Represents the active state of the note
        };
        struct cs_t {                   // Struct cs_t represents the chip select pin for SPI devices
            uint8_t port;
            uint8_t pin;
        };
        struct key_t ir_keys[KEY_COUNT];
        struct cs_t adc_cs_list[ADC_COUNT];
        void read_keys( struct key_t *keys, struct cs_t *cs_list );    	// Read and store values from keys
        void process_keys( struct key_t *key );							// Process keys and determine which note(s) to play
        uint16_t read_adc( cs_t *cs, uint8_t ch );						// Read values from ADC
        void update_leds( void ); 										// Update LEDs of keys; activation based on the active state of keys
    public:
        Ir_key_task( uint8_t priority ) : scheduler_task( "Ir_key_task", ( 512 * 2 ), priority ) { }
        bool init( void );
        bool run( void *p );
};

class Control_task : public scheduler_task
{
    public:
        Control_task( uint8_t priority ) : scheduler_task( "Control_Task", ( 512 * 2 ), priority ) { }
        bool init( void );
        bool run( void *p );
};

#endif /* ELECTRONIC_PIANO_TASKS_HPP_ */
