DDS (direct-digital synthesis) / エンベロープジェネレーター / キー入力で割り込みをかける

前回はポーリング方式でキー入力を監視したが、今回はキー入力の受信時に割り込みをかけてキー入力を取り込む。違いは認識できない。

#include <avr/io.h>
#include <avr/interrupt.h>
#include "scale.h"
extern "C"{
    #include "USART.h"
}

#define FULL_VOL       31    // 最大音量。5ビット値(0~31)。
#define ATTACK_RATE    50    // 小さいほど鋭く最大音量まで立ち上がる。
#define DECAY_RATE     300   // 小さいほど鋭くsustainレベルまで減衰する。
#define SUSTAIN_LEVEL  10
#define SUSTAIN_TIME   10000
#define RELEASE_RATE   500   // 小さいほど鋭く音量ゼロまで減衰する。

#define ATTACK_TIME  (ATTACK_RATE * FULL_VOL)
#define DECAY_TIME   (ATTACK_TIME + (FULL_VOL - SUSTAIN_LEVEL) * DECAY_RATE)
#define RELEASE_TIME (DECAY_TIME + SUSTAIN_TIME)

volatile uint16_t tuningWord = C1;
volatile uint16_t noteClock  = 0;
ISR(USART_RX_vect){
    char serialInput = UDR0;
    printWord(serialInput);
    tuningWord  = lookupPitch(serialInput);
    noteClock   = 1;
}

void initRxCompleteInterrupt(){
    UCSR0B |= (1 << RXCIE0); // receive complete割り込みを有効化する。
    sei();                   // グローバル割り込みを有効化する。
}   

void createSaw(int8_t* tbl){
    for(int i=0; i<256; i++){
        tbl[i] = (int8_t)i;
    }
}

void initTimer0(){
    TCCR0A |= (1 << WGM00) | (1 << WGM01); // Fast PWMモード
    TCCR0A |= (1 << COM0A1);               // コンペアマッチ時にOC0A (PD6)をクリア、ボトム時にOC0A (PD6)をセット。
    TCCR0B |= (1 << CS00);                 // 1分周。8 MHzをそのままカウントする。
}

uint16_t lookupPitch(char ch){
    switch(ch){
        case 'a': return(G1);
        case 's': return(A1);
        case 'd': return(B1);
        case 'f': return(C2);
        case 'g': return(D2);
        case 'h': return(E2);
        case 'j': return(F2);
        case 'k': return(G2);
        case 'l': return(A2);
        case ';': return(B2);
        case ':': return(C3);
        default : return(C1);
    }
}

int main(void) {
    int8_t   Saw[256]; createSaw(Saw); // のこぎり波を1周期分作る。
    uint16_t accumulator = 0;
    uint8_t  vol         = 0;
    int16_t  mixer       = 0;
    uint8_t  waveStep;
    char isInAttack, isInDecay, isInRelease, AdsrComplete;

    initTimer0();
    initUSART();
    initRxCompleteInterrupt();

    DDRD |= (1 << PD6); // PD6 (OC0A)にスピーカーをつないでそこからDDS (pwm)を出力することにする。

    while(1){

        accumulator += tuningWord;
        waveStep     = accumulator >> 8;
        mixer        = Saw[waveStep] * vol;
        mixer      >>= 5;

        if(noteClock){
            (noteClock < ATTACK_TIME) && (noteClock > ATTACK_RATE * vol) && (vol < FULL_VOL)?
                isInAttack = 1:
                isInAttack = 0;
            (noteClock < DECAY_TIME) && ((noteClock - ATTACK_TIME) > (FULL_VOL - vol) * DECAY_RATE) && (vol > SUSTAIN_LEVEL)?
                isInDecay = 1:
                isInDecay = 0;
            (noteClock > RELEASE_TIME) && ((noteClock - RELEASE_TIME) > (SUSTAIN_LEVEL - vol) * RELEASE_RATE) && (vol > 0)?
                isInRelease = 1:
                isInRelease = 0;
            (noteClock > RELEASE_TIME) && ((noteClock - RELEASE_TIME) > (SUSTAIN_LEVEL - vol) * RELEASE_RATE) && (vol <= 0)?
                AdsrComplete = 1:
                AdsrComplete = 0;

            noteClock++;                          // ADSRの4フェーズが終了するまでインクリメントし続ける。
            if     (isInAttack)  {vol++;}         // attackフェーズが終わるまで音量を上げ続ける。
            else if(isInDecay)   {vol--;}         // decayフェーズが終わるまで音量を下げ続ける。
            else if(isInRelease) {vol--;}         // releaseフェーズが終わるまで音量を下げ続ける。
            else if(AdsrComplete){noteClock = 0;} // ADSRの4フェーズが終わったら、
        }

        loop_until_bit_is_set(TIFR0, TOV0); // タイマーの1周期が終わるのを待ってから、
        OCR0A  = 128 + (uint8_t)mixer;      // 次のパルス幅をセットして、
        TIFR0 |= (1 << TOV0);               // オーバーフローフラグを手動でクリアする。

    }
     
    return 0;
}