Integrated version

BaseMachineUIController.h

Committer:
ryood
Date:
2017-05-04
Revision:
18:6ba18820777d
Parent:
17:5c8c173008cf

File content as of revision 18:6ba18820777d:

/*
 * BaseMachineUIController.h
 *
 * 2016.11.06 created
 *
 */
 
#define UART_TRACE_EEPROM    (0)
#define UART_TRACE_CLIPBOARD (1)

#ifndef _UICONTROLLER_H_
#define _UICONTROLLER_H_

#include "mbed.h"
#include "rtos.h"

#include "BaseMachineCommon.h"

#include "st7565LCD.h"
#include "AT24C1024.h"
#include "PinDetect.h"
#include "RotaryEncoder.h"
#include "AverageAnalogIn.h"
#include "ExioBufferedDebounceIn.h"
#include "ExioBufferedRotaryEncoder.h"

#include "Sequence.h"
#include "ST7565_SequencerDisplay.h"

#define GLCD_SPI_RATE       (1000000)
#define POT_RESOLUTION      (7)     // bit
#define AIN_AVERAGE         (16)    // AnalogInを移動平均する要素数

const int bpmMax = 240;
const int bpmMin = 60;
const int octaveMax = 2;
const int octaveMin = -2;
const int waveShapeMax = 1;
const int UImodeMax = 2;
const int EEPROMSlotMax = 16;

const int ExioPinToPattern[] = {
    0, 4, 1, 5, 2, 6, 3, 7
};

typedef enum {
    flagEepromOK           = 0x04,
    flagEepromCancel       = 0x08,
    flagSaveToEeprom       = 0x10,
    flagLoadFromEeprom     = 0x20,
    flagCopyToClipBoard    = 0x40,
    flagPasteFromClipBoard = 0x80
} LoadSaveFlagT;

struct OscillatorParam {
    uint8_t waveShape;
    uint8_t pulseWidth;

    OscillatorParam() : waveShape(0), pulseWidth(0) {}
};

struct FilterParam {
    uint8_t cutoff;
    uint8_t resonance;

    FilterParam() : cutoff(0), resonance(0) {}
};

struct EnvelopeParam {
    uint8_t level;
    uint8_t length;
    uint8_t duration;
    uint8_t decay;
    uint8_t sustain;

    EnvelopeParam() : level(0), length(0), duration(0), decay(0), sustain(0) {}
};

class BaseMachineUIController
{
public:
    BaseMachineUIController() :
        bpm(120),
        accentLevel(127),
        sequencePattern(0),
        loadSaveFlag(0),
        eepromSlot(0),
        editingStep(0),
        playingStep(0),
        isRunning(false),
        isDirty(true),
        isClipBoardEmpty(true),
        UImode(0),
        isStepChanged(false),
        errCutoff(0),
        errDuration(0),
        errDecay(0),
        errSustain(0),
        errResonance(0),
        errLevel(0),
        errPulseWidth(0),
        errAccentLevel(0) {
    }
    
    ~BaseMachineUIController() {}

    void init() {
        //--------------------------------------------------------------------
        // Create Dvices
        //
        //ST7565(PinName mosi, PinName sclk, PinName cs, PinName rst, PinName a0);
        gLCD = new ST7565(PB_15, PB_13, PB_12, PB_2, PB_1);
        sequencerDisplay = new ST7565_SequencerDisplay(gLCD, sequences[sequencePattern], SEQUENCE_N);

        AinPulseWidth = new AverageAnalogIn(PC_2, AIN_AVERAGE);
        AinCutOff     = new AverageAnalogIn(PB_0, AIN_AVERAGE);
        AinResonance  = new AverageAnalogIn(PC_1, AIN_AVERAGE);
        AinLevel      = new AverageAnalogIn(PC_0, AIN_AVERAGE);
        AinDuration   = new AverageAnalogIn(PA_4, AIN_AVERAGE);
        AinDecay      = new AverageAnalogIn(PA_1, AIN_AVERAGE);
        AinSustain    = new AverageAnalogIn(PA_0, AIN_AVERAGE);
        AinAccentLevel= new AverageAnalogIn(PC_3, AIN_AVERAGE);

        RotEncStep  = new RotaryEncoder(PA_11, PA_12, 0, SEQUENCE_N - 1, 0);
        RotEncPitch = new RotaryEncoder(PB_5, PB_4, 0, Sequence::getMaxPitch() - 1, 0);
        //RotEncBpm   = new RotaryEncoder(PC_12, PC_10, bpmMin, bpmMax, 120);
        RotEncBpm   = new RotaryEncoder(PA_14, PA_15, bpmMin, bpmMax, 120);

        //PinWaveShape  = new PinDetect(PD_2, PullUp);
        PinWaveShape  = new PinDetect(PB_7, PullUp);
        //PinUIMode     = new PinDetect(PC_11, PullUp);
        PinUIMode     = new PinDetect(PC_13, PullUp);
        PinOctaveUp   = new PinDetect(PB_3, PullUp);
        PinOctaveDown = new PinDetect(PA_10, PullUp);
        PinNoteOnOff  = new PinDetect(PC_5, PullUp);
        PinTie        = new PinDetect(PC_6, PullUp);
        PinAccent     = new PinDetect(PC_8, PullUp);
        PinRunStop    = new PinDetect(PC_9, PullUp);
        
        Spi3 = new SPI(PC_12, PC_11, PC_10);    // SPI3 mosi, miso, sclk;
        // ExioMcp23s17(int hardwareaddress, SPI& spi, PinName nCs, PinName nReset);
        Exio = new ExioMcp23s17(0x00, *Spi3, PD_2, PA_13);
        Exio->reset();
        
        ExioInBufferA = new ExioInBuffer(Exio, ExioPortA);
        for (int i = 2; i < 8; i++) {
            ExioInA[i] = new ExioBufferedDebounceIn(ExioInBufferA, i);
        }

        ExioInBufferB = new ExioInBuffer(Exio, ExioPortB);
        for (int i = 0; i < 8; i++) {
            ExioIn[i] = new ExioBufferedDebounceIn(ExioInBufferB, i);
        }

        ExioRotEnc1 = new ExioBufferedRotaryEncoder(ExioInBufferA, 0, 1, 0, EEPROMSlotMax - 1, 0);
        
        I2c1 = new I2C(PB_9, PB_8);
        At24c1024 = new AT24C1024(*I2c1);
        
        //--------------------------------------------------------------------
        // Setup Devices
        //
        gLCD->set_spi_frequency(GLCD_SPI_RATE);
        gLCD->begin(0x12);
        gLCD->clear();
        gLCD->drawstring(0, 0, TITLE_STR1);
        gLCD->drawstring(0, 1, TITLE_STR2);
        gLCD->drawstring(0, 2, TITLE_STR3);
        gLCD->display();
        Thread::wait(1000);

        RotEncStep->setInterval(100);
        RotEncPitch->setInterval(100);
        RotEncBpm->setInterval(100);

        PinWaveShape->attach_asserted(this, &BaseMachineUIController::swWaveShapePressed);
        PinWaveShape->setAssertValue(0);
        PinWaveShape->setSampleFrequency();

        PinUIMode->attach_asserted(this, &BaseMachineUIController::swUIModePressed);
        PinUIMode->setAssertValue(0);
        PinUIMode->setSampleFrequency();

        PinOctaveUp->attach_asserted(this, &BaseMachineUIController::swOctaveUpPressed);
        PinOctaveUp->setAssertValue(0);
        PinOctaveUp->setSampleFrequency();

        PinOctaveDown->attach_asserted(this, &BaseMachineUIController::swOctaveDownPressed);
        PinOctaveDown->setAssertValue(0);
        PinOctaveDown->setSampleFrequency();

        PinNoteOnOff->attach_asserted(this, &BaseMachineUIController::swNoteOnOffPressed);
        PinNoteOnOff->setAssertValue(0);
        PinNoteOnOff->setSampleFrequency();

        PinTie->attach_asserted(this, &BaseMachineUIController::swTiePressed);
        PinTie->setAssertValue(0);
        PinTie->setSampleFrequency();

        PinAccent->attach_asserted(this, &BaseMachineUIController::swAccentPressed);
        PinAccent->setAssertValue(0);
        PinAccent->setSampleFrequency();

        PinRunStop->attach_asserted(this, &BaseMachineUIController::swRunStopPressed);
        PinRunStop->setAssertValue(0);
        PinRunStop->setSampleFrequency();
        
        ExioInBufferA->run(10);
        for (int i = 2; i < 8; i++) {
            ExioInA[i]->set_debounce_us(5000);
        }
        
        ExioInBufferB->run(10);
        for (int i = 0; i < 8; i++) {
            ExioIn[i]->set_debounce_us(5000);
        }

        ExioRotEnc1->setInterval(1000);
        
        //--------------------------------------------------------------------
        // Initialize objects
        //
        Sequence::setBaseNoteNumber(baseNoteNumber);

        for (int j = 0; j < PATTERN_N; j++) {
            for (int i = 0; i < SEQUENCE_N; i++) {
                sequences[j][i].setPitch(pitch[i]);
                sequences[j][i].setNoteOn(noteOn[i]);
                sequences[j][i].setTie(tie[i]);
                sequences[j][i].setAccent(accent[i]);
            }
        }
        RotEncPitch->setVal(sequences[sequencePattern][0].getPitch());
        
        for (int i = 0; i < SEQUENCE_N; i++) {
            sequenceClipBoard[i].setPitch(pitch[i]);
            sequenceClipBoard[i].setNoteOn(noteOn[i]);
            sequenceClipBoard[i].setTie(tie[i]);
            sequenceClipBoard[i].setAccent(accent[i]);
        }
    }

    void destract() {
        //--------------------------------------------------------------------
        // Destract Devices
        //
        delete gLCD;

        delete AinPulseWidth;
        delete AinCutOff;
        delete AinResonance;
        delete AinLevel;
        delete AinDuration;
        delete AinDecay;
        delete AinSustain;
        delete AinAccentLevel;

        delete RotEncStep;
        delete RotEncPitch;
        delete RotEncBpm;

        delete PinWaveShape;
        delete PinUIMode;
        delete PinOctaveUp;
        delete PinOctaveDown;
        delete PinNoteOnOff;
        delete PinTie;
        delete PinAccent;
        delete PinRunStop;
        
        delete[] ExioInA;
        delete ExioRotEnc1;
        delete[] ExioIn;
        delete ExioInBufferA;
        delete ExioInBufferB;
        delete Exio;
        delete Spi3;
        
        delete At24c1024;
        delete I2c1;
    }
    
    void getSequences(Sequence (*pSequences)[SEQUENCE_N]) {
        memcpy(pSequences, sequences[sequencePattern], sizeof(sequences[sequencePattern]));
    }
    /*
    void setSequences(Sequence* pSequence) {
        memcpy(sequences[sequencePattern], pSequence, sizeof(sequences[sequencePattern]));
    }
    */
    
    void getOscillatorParam(OscillatorParam* pParam) {
        memcpy(pParam, &oscillatorParam, sizeof(oscillatorParam));
    }
    void getFilterParam(FilterParam* pParam) {
        memcpy(pParam, &filterParam, sizeof(filterParam));
    }   
    void getEnvelopeParam(EnvelopeParam* pParam) {
        memcpy(pParam, &envelopeParam, sizeof(envelopeParam));
    }
    uint8_t getBpm() { return bpm; }
    uint8_t getAccentLevel() { return accentLevel; }
    uint8_t getSequencePattern() { return sequencePattern; }
    
    bool getIsRunning() { return isRunning; }
    
    void setPlayingStep(int step)
    {
        playingStep = step;
        isDirty = true;
    }
    
    void update() {
        pollingRotEncs();
        pollingPots();
        pollingExio();

        switch (UImode) {
            case 0:
                if (isDirty) {
                    if (isRunning) {
                        sequencerDisplay->update(SequencerDisplay::run, editingStep, playingStep);
                    } else {
                        sequencerDisplay->update(SequencerDisplay::stop, editingStep, playingStep);
                    }
                    isDirty = false;
                }
                break;
            case 1:
                dumpToLCD00();
                break;
            case 2:
                dumpToLCD01();
                break;
            case 3:
                dumpToLCD02();
                break;
        }
        
        switch (loadSaveFlag) {
        case flagSaveToEeprom:
            saveToEeprom();
            Thread::wait(500);
            isDirty = true;
            break;
        case flagLoadFromEeprom:
            loadFromEeprom();
            Thread::wait(500);
            isDirty = true;
            break;
        case flagCopyToClipBoard:
            copyToClipBoard();
            Thread::wait(10);
            isDirty = true;
            break;
        case flagPasteFromClipBoard:
            pasteFromClipBoard();
            Thread::wait(10);
            isDirty = true;
            break;
        }
    }

private:
    ST7565* gLCD;
    
    SPI* Spi3;
    ExioMcp23s17* Exio;
    ExioInBuffer* ExioInBufferA;
    ExioInBuffer* ExioInBufferB;
    ExioBufferedDebounceIn* ExioInA[8];
    ExioBufferedDebounceIn* ExioIn[8];
    ExioBufferedRotaryEncoder* ExioRotEnc1;
    
    I2C* I2c1;
    AT24C1024* At24c1024;

    AverageAnalogIn* AinPulseWidth;
    AverageAnalogIn* AinCutOff;
    AverageAnalogIn* AinResonance;
    AverageAnalogIn* AinLevel;
    AverageAnalogIn* AinDuration;
    AverageAnalogIn* AinDecay;
    AverageAnalogIn* AinSustain;
    AverageAnalogIn* AinAccentLevel;

    RotaryEncoder* RotEncStep;
    RotaryEncoder* RotEncPitch;
    RotaryEncoder* RotEncBpm;

    PinDetect* PinWaveShape;
    PinDetect* PinUIMode;
    PinDetect* PinOctaveUp;
    PinDetect* PinOctaveDown;
    PinDetect* PinNoteOnOff;
    PinDetect* PinTie;
    PinDetect* PinAccent;
    PinDetect* PinRunStop;

    ST7565_SequencerDisplay* sequencerDisplay;
    
    Sequence sequences[PATTERN_N][SEQUENCE_N];
    Sequence sequenceClipBoard[SEQUENCE_N];
    
    OscillatorParam oscillatorParam;
    FilterParam filterParam;
    EnvelopeParam envelopeParam;
    
    uint8_t bpm;
    uint8_t accentLevel;
    uint8_t sequencePattern;
    uint8_t loadSaveFlag;
    uint8_t eepromSlot;

    int editingStep;
    int playingStep;
    bool isRunning;
    bool isDirty;
    bool isClipBoardEmpty;
    int UImode;

    bool isStepChanged;

    int errCutoff;
    int errDuration;
    int errDecay;
    int errSustain;
    int errResonance;
    int errLevel;
    int errPulseWidth;
    int errAccentLevel;

    //------------------------------------------------------------------------
    // PinDetect callback functions
    //------------------------------------------------------------------------
    void swWaveShapePressed() {
#if (UART_TRACE)
        printf("PinWaveShape Pushed\r\n");
#endif
        uint8_t _waveShape = oscillatorParam.waveShape;
        _waveShape++;
        if (_waveShape > waveShapeMax) {
            _waveShape = 0;
        }
        oscillatorParam.waveShape = _waveShape;
        sequencerDisplay->setWaveShape(_waveShape);
        isDirty = true;
    }

    void swUIModePressed() {
#if (UART_TRACE)
        printf("PinUIMode Pushed\r\n");
#endif
        UImode++;
        if (UImode > UImodeMax) {
            UImode = 0;
        }
        isDirty = true;
    }

    void swOctaveUpPressed() {
#if (UART_TRACE)
        printf("PinOctaveUp Pushed\r\n");
#endif
        if (sequencerDisplay->getOctave() < octaveMax) {
            sequencerDisplay->setOctave(sequencerDisplay->getOctave() + 1);
            isDirty = true;
        }
    }

    void swOctaveDownPressed() {
#if (UART_TRACE)
        printf("PinOctaveDown Pushed\r\n");
#endif
        if (sequencerDisplay->getOctave () > octaveMin) {
            sequencerDisplay->setOctave(sequencerDisplay->getOctave() - 1);
            isDirty = true;
        }
    }

    void swNoteOnOffPressed() {
#if (UART_TRACE)
        printf("PinNoteOnOff Pushed\r\n");
#endif
        sequences[sequencePattern][editingStep].setNoteOn(!sequences[sequencePattern][editingStep].isNoteOn());
        isDirty = true;
    }

    void swTiePressed() {
#if (UART_TRACE)
        printf("PinTie Pushed\r\n");
#endif
        sequences[sequencePattern][editingStep].setTie(!sequences[sequencePattern][editingStep].isTie());
        isDirty = true;
    }

    void swAccentPressed() {
#if (UART_TRACE)
        printf("PinAccent Pushed\r\n");
#endif
        sequences[sequencePattern][editingStep].setAccent(!sequences[sequencePattern][editingStep].isAccent());
        isDirty = true;
    }

    void swRunStopPressed() {
#if (UART_TRACE)
        printf("PinRunStop Pushed\r\n");
#endif
        if (isRunning) {
            isRunning = false;
        } else {
            isRunning = true;
        }
        isDirty = true;
    }

    //------------------------------------------------------------------------
    // Functions
    //------------------------------------------------------------------------
    void pollingRotEncs() {
        int _bpm = RotEncBpm->getVal();
        if (_bpm != bpm) {
            bpm = _bpm;
            sequencerDisplay->setBpm(_bpm);
            isDirty = true;
        }

        int _step = RotEncStep->getVal();
        if (_step != editingStep) {
            editingStep = _step;
            // syncronize sequence value & Rotary Encoder's value
            RotEncPitch->setVal(sequences[sequencePattern][editingStep].getPitch());
            isDirty = true;
        }

        int _pitch = RotEncPitch->getVal();
        if (_pitch != sequences[sequencePattern][editingStep].getPitch()) {
            sequences[sequencePattern][editingStep].setPitch(_pitch);
            isDirty = true;
        }
    }

    void pollingPots() {
        uint8_t pulseWidth = AinPulseWidth->read_u16() >> (16 - POT_RESOLUTION);
        if (pulseWidth != oscillatorParam.pulseWidth) {
            oscillatorParam.pulseWidth = pulseWidth;
            errPulseWidth++;
            //isDirty = true;
        }

        uint8_t cutoff = AinCutOff->read_u16() >> (16 - POT_RESOLUTION);
        if (cutoff != filterParam.cutoff) {
            filterParam.cutoff = cutoff;
            errCutoff++;
            //isDirty = true;
        }

        uint8_t resonance = AinResonance->read_u16() >> (16 - POT_RESOLUTION);
        if (resonance != filterParam.resonance) {
            filterParam.resonance = resonance;
            errResonance++;
            //isDirty = true;
        }

        uint8_t level = AinLevel->read_u16() >> (16 - POT_RESOLUTION);
        if (level != envelopeParam.level) {
            errLevel++;
            envelopeParam.level = level;
            //isDirty = true;
        }

        uint8_t duration = AinDuration->read_u16() >> (16 - POT_RESOLUTION);
        if (duration != envelopeParam.duration) {
            errDuration++;
            envelopeParam.duration = duration;
            //isDirty = true;
        }

        uint8_t decay = AinDecay->read_u16() >> (16 - POT_RESOLUTION);
        if (decay != envelopeParam.decay) {
            errDecay++;
            envelopeParam.decay = decay;
            //isDirty = true;
        }

        uint8_t sustain = AinSustain->read_u16() >> (16 - POT_RESOLUTION);
        if (sustain != envelopeParam.sustain) {
            errSustain++;
            envelopeParam.sustain = sustain;
            //isDirty = true;
        }

        uint8_t _accentLevel = AinAccentLevel->read_u16() >> (16 - POT_RESOLUTION);
        if (_accentLevel != accentLevel) {
            errAccentLevel++;
            accentLevel = _accentLevel;
            //isDirty = true;
        }
    }
    
    void pollingExio()
    {
        int i;
        for (i = 0; i < 8; i++) {
            if (ExioIn[i]->read())  break;
        }
        if (i != 8 && sequencePattern != ExioPinToPattern[i]) {
            sequencePattern = ExioPinToPattern[i];
            // syncronize sequence value & Rotary Encoder's value
            RotEncPitch->setVal(sequences[sequencePattern][editingStep].getPitch());
            
            sequencerDisplay->setSequences(sequences[sequencePattern]);
            sequencerDisplay->setSequencePattern(sequencePattern);
            isDirty = true;
        }
        
        loadSaveFlag = 0;
        for (i = 2; i < 8; i++) {
            loadSaveFlag |= ((ExioInA[i]->read()) << i);
        }
        
        #if (UART_TRACE)
        if (loadSaveFlag != 0) {
            printf("loadSaveFlag: %d\r\n", loadSaveFlag);
        }
        #endif
        
        eepromSlot = ExioRotEnc1->getVal();
    }
    
    void dumpToLCD00() {
        char buff[64];
        int col = 0;

        gLCD->clear();

        sprintf(buff, "Run:%d BPM:%03d Ptn:%02d", isRunning, bpm, sequencePattern + 1);
        gLCD->drawstring(0, col++, buff);

        sprintf(buff, "Stp:%02d Nto:%d Pch:%02d",
                editingStep, sequences[sequencePattern][editingStep].isNoteOn(), sequences[sequencePattern][editingStep].getPitch());
        gLCD->drawstring(0, col++, buff);

        sprintf(buff, "Oct:%-2d Tie:%d Acc:%d",
                sequencerDisplay->getOctave(), sequences[sequencePattern][editingStep].isTie(),sequences[sequencePattern][editingStep].isAccent());
        gLCD->drawstring(0, col++, buff);

        sprintf(buff, "Wsp:%d Mod:%d pStp:%d", oscillatorParam.waveShape, UImode, playingStep);
        gLCD->drawstring(0, col++, buff);

        sprintf(buff, "LSF:%d SLT:%d", loadSaveFlag, eepromSlot);
        gLCD->drawstring(0, col++, buff);

        col++;
        gLCD->drawstring(0, col++, TITLE_STR2);
        gLCD->drawstring(0, col++, TITLE_STR3);
        
        /*
        sprintf(buff, "RSO:%4d   CO :%4d", filterParam.resonance, filterParam.cutoff);
        gLCD->drawstring(0, col++, buff);
        sprintf(buff, "LVL:%4d   DUR:%4d", envelopeParam.level, envelopeParam.duration);
        gLCD->drawstring(0, col++, buff);
        sprintf(buff, "PW :%4d   DCY:%4d", oscillatorParam.pulseWidth, envelopeParam.decay);
        gLCD->drawstring(0, col++, buff);
        sprintf(buff, "ACL:%4d   SUS:%4d", accentLevel, envelopeParam.sustain);
        gLCD->drawstring(0, col++, buff);
        */

        gLCD->display();
    }

    void dumpToLCD01()
    {
        char buff[64];

        gLCD->clear();

        sprintf(buff, "Cutoff     %3d %5d", filterParam.cutoff, errCutoff);
        gLCD->drawstring(0, 0, buff);
        sprintf(buff, "Duration   %3d %5d", envelopeParam.duration, errDuration);
        gLCD->drawstring(0, 1, buff);
        sprintf(buff, "Decay      %3d %5d", envelopeParam.decay, errDecay);
        gLCD->drawstring(0, 2, buff);
        sprintf(buff, "Sustain    %3d %5d", envelopeParam.sustain, errSustain);
        gLCD->drawstring(0, 3, buff);
        sprintf(buff, "Resonance  %3d %5d", filterParam.resonance, errResonance);
        gLCD->drawstring(0, 4, buff);
        sprintf(buff, "Level      %3d %5d", envelopeParam.level, errLevel);
        gLCD->drawstring(0, 5, buff);
        sprintf(buff, "PulseWidth %3d %5d", oscillatorParam.pulseWidth, errPulseWidth);
        gLCD->drawstring(0, 6, buff);
        sprintf(buff, "AccentLvl  %3d %5d", accentLevel, errAccentLevel);
        gLCD->drawstring(0, 7, buff);

        gLCD->display();
    }
    
    void dumpToLCD02()
    {
        char buff[64];

        gLCD->clear();

        sprintf(buff, "sequencePattern %d", sequencePattern);
        gLCD->drawstring(0, 0, buff);
        
        gLCD->display();
    }
    
    void saveToEeprom()
    {
        char buff[64];

        gLCD->clear();

        sprintf(buff, "Save to EEPROM?[%02d]", eepromSlot);
        gLCD->drawstring(0, 0, buff);
        gLCD->display();
        
        while (1) {
            pollingExio();
            sprintf(buff, "[%02d]", eepromSlot);
            gLCD->drawstring(90, 0, buff);
            gLCD->display();
            //ToDo: flagEepromCancel スイッチを増やして割り当てる
            if (loadSaveFlag & flagEepromCancel) {
                gLCD->drawstring(0, 1, " Cancel");
                gLCD->display();  
                return;
            }
            //printf("loadSaveFlag: %2d\r\n", loadSaveFlag);
            if (loadSaveFlag & flagEepromOK) {
                break;
            }
        }
        
        gLCD->drawstring(0, 1, " Saving...");
        gLCD->display();    
        
        #if (UART_TRACE_EEPROM)
        printf("*** Save Sequences to EEPROM ***\r\n");
        for (int j = 0; j < PATTERN_N; j++) {
            printf("eepromSlot: %d\r\n", eepromSlot);
            printf("sequencePattern: %d\r\n", j);
            for (int i = 0; i < SEQUENCE_N; i++) {
                printf("%d %d %03d %d %d\r\n",
                    i,
                    sequences[j][i].isNoteOn(),
                    sequences[j][i].getPitch(),
                    sequences[j][i].isTie(),
                    sequences[j][i].isAccent()
                );
            }
        }
        #endif
        
        uint8_t eep_buff[258];
        for (int j = 0; j < PATTERN_N; j++) {
            uint8_t cnt = 0;
            for (int i = 0; i < SEQUENCE_N; i++) {
                eep_buff[cnt++] = sequences[j][i].isNoteOn();
                eep_buff[cnt++] = ((sequences[j][i].getPitch() & 0xff00) >> 8); // MSB
                eep_buff[cnt++] = sequences[j][i].getPitch() & 0xff;            // LSB
                eep_buff[cnt++] = sequences[j][i].isTie();
                eep_buff[cnt++] = sequences[j][i].isAccent();
            }
            #if (UART_TRACE_EEPROM)
            printf("size: %d\r\n", cnt);
            #endif
            /*
            for (int i = 0; i < cnt; i++) {
                printf("%d: %d\r\n", i, eep_buff[i]);
            }
            */
            uint32_t addr_page_top = (eepromSlot * PATTERN_N + j) << 8;
            #if (UART_TRACE_EEPROM)
            printf("address: %x\r\n", addr_page_top);
            #endif 
            AT24C_STATUS status = At24c1024->write_page(
                addr_page_top, 
                eep_buff,
                sizeof(eep_buff)
            );
            Thread::wait(5);
            
            #if (UART_TRACE_EEPROM)
            printf("status: %d\r\n", status);
            #endif
        }

        gLCD->drawstring(0, 2, " Done");
        gLCD->display();    
    }
    
    void loadFromEeprom()
    {
        char buff[64];

        gLCD->clear();

        sprintf(buff, "Load From EEPROM?[%02d]", eepromSlot);
        gLCD->drawstring(0, 0, buff);
        gLCD->display();
        
        while (1) {
            pollingExio();
            sprintf(buff, "[%02d]", eepromSlot);
            gLCD->drawstring(102, 0, buff);
            gLCD->display();
            //ToDo: flagEepromCancel スイッチを増やして割り当てる
            if (loadSaveFlag & flagEepromCancel) {
                gLCD->drawstring(0, 1, " Cancel");
                gLCD->display();  
                return;
            }
            if (loadSaveFlag &= flagEepromOK) {
                break;
            }
        }
        
        gLCD->drawstring(0, 1, " Loading...");
        gLCD->display();  
        
        #if (UART_TRACE_EEPROM)
        printf("*** Load Sequences from EEPROM ***\r\n");
        #endif
        
        uint8_t eep_buff[258];
        
        for (int j = 0; j < PATTERN_N; j++) {
            uint32_t addr_page_top = (eepromSlot * PATTERN_N + j) << 8;

            #if (UART_TRACE_EEPROM)
            printf("address: %x\r\n", addr_page_top);
            #endif

            AT24C_STATUS status = At24c1024->read_page(
                addr_page_top, 
                eep_buff,
                sizeof(eep_buff)
            );
        
            #if (UART_TRACE_EEPROM)
            printf("status: %d\r\n", status);
            #endif
        
            /*
            for (int i = 0; i < 80; i++) {
                printf("%d: %d\r\n", i, eep_buff[i]);
            }
            */
        
            uint8_t cnt = 0;
            for (int i = 0; i < SEQUENCE_N; i++) {
                sequences[j][i].setNoteOn(eep_buff[cnt++]);
                int pitch = eep_buff[cnt++] << 8;   // MSB
                pitch |= eep_buff[cnt++];           // LSB
                sequences[j][i].setPitch(pitch);
                sequences[j][i].setTie(eep_buff[cnt++]);
                sequences[j][i].setAccent(eep_buff[cnt++]);
            }
            
            #if (UART_TRACE_EEPROM)
            printf("size: %d\r\n", cnt);
            printf("eepromSlot: %d\r\n", eepromSlot);
            printf("sequencePattern: %d\r\n", j);
            #endif
        }
        // syncronize sequence value & Rotary Encoder's value
        RotEncPitch->setVal(sequences[sequencePattern][editingStep].getPitch());

        #if (UART_TRACE_EEPROM)
        for (int j = 0; j < PATTERN_N; j++) {
            for (int i = 0; i < SEQUENCE_N; i++) {
                printf("%d %d %03d %d %d\r\n",
                    i,
                    sequences[j][i].isNoteOn(),
                    sequences[j][i].getPitch(),
                    sequences[j][i].isTie(),
                    sequences[j][i].isAccent()
                );
            }
        }
        #endif

        gLCD->drawstring(0, 2, " Done");
        gLCD->display();  
    }
    
    void copyToClipBoard()
    {
        gLCD->clear();
        gLCD->drawstring(0, 0, "Copy");
        gLCD->display();

        for (int i = 0; i < SEQUENCE_N; i++) {
            sequenceClipBoard[i].setNoteOn(sequences[sequencePattern][i].isNoteOn());
            sequenceClipBoard[i].setPitch(sequences[sequencePattern][i].getPitch());
            sequenceClipBoard[i].setTie(sequences[sequencePattern][i].isTie());
            sequenceClipBoard[i].setAccent(sequences[sequencePattern][i].isAccent());
        }
        isClipBoardEmpty = false;
           
        #if (UART_TRACE_CLIPBOARD)
        printf("copyToClipBoard\r\n");
        printf("sequencePattern: %d\r\n", sequencePattern);
        for (int i = 0; i < SEQUENCE_N; i++) {
            printf("%d %d %03d %d %d\r\n",
                i,
                sequenceClipBoard[i].isNoteOn(),
                sequenceClipBoard[i].getPitch(),
                sequenceClipBoard[i].isTie(),
                sequenceClipBoard[i].isAccent()
            );
        }
        #endif
    }
    
    void pasteFromClipBoard()
    {
        gLCD->clear();
        gLCD->drawstring(0, 0, "Paste");
        gLCD->display();  

        #if (UART_TRACE_CLIPBOARD)
        printf("pasteFromClipBoard\r\n");
        printf("isClipBoardEmpty: %d\r\n", isClipBoardEmpty);
        printf("sequencePattern: %d\r\n", sequencePattern);
        for (int i = 0; i < SEQUENCE_N; i++) {
            printf("%d %d %03d %d %d\r\n",
                i,
                sequenceClipBoard[i].isNoteOn(),
                sequenceClipBoard[i].getPitch(),
                sequenceClipBoard[i].isTie(),
                sequenceClipBoard[i].isAccent()
            );
        }
        #endif
        
        for (int i = 0; i < SEQUENCE_N; i++) {
            if (!isClipBoardEmpty) {
                sequences[sequencePattern][i].setNoteOn(sequenceClipBoard[i].isNoteOn());
                sequences[sequencePattern][i].setPitch(sequenceClipBoard[i].getPitch());
                sequences[sequencePattern][i].setTie(sequenceClipBoard[i].isTie());
                sequences[sequencePattern][i].setAccent(sequenceClipBoard[i].isAccent());
            }
        }
        // syncronize sequence value & Rotary Encoder's value
        RotEncPitch->setVal(sequences[sequencePattern][editingStep].getPitch());
    }
};

#endif //_UICONTROLLER_H_