xypad theremin for LPC1768

Dependencies:   MODDMA mbed

note.cpp

Committer:
exopiped
Date:
2016-03-14
Revision:
2:c5eeaf1c8e69
Parent:
1:aa184d2eb2e3

File content as of revision 2:c5eeaf1c8e69:

/* 
 *  note.cpp -- manage production of notes in response to
 *     touch screen input
 */
#include "mbed.h"
#include "InterruptIn.h"
#include "debug.h"
#include "dma.h"
#include "envlp.h"
#include "wave.h"
#include "touch.h"
#include "jswitch.h"
#include "note.h"

#define TURN_DMA_OFF_AFTER_EACH_NOTE 0

typedef enum {NOTE_ATTACK,NOTE_DECAY,NOTE_SUSTAIN,NOTE_RELEASE,NOTE_OFF} NOTE_STATE;

static void note_start(void);
static void note_state_machine(void);
static void note_fill_buf(void);
static void note_attack(void);
static bool note_attack_done(void);
static bool note_decay_done(void);
static void note_release(void);
static bool note_release_done(void);
static bool note_released(void);
static void note_end(void);

static unsigned note_freq=0;       // frequency in Hz
static NOTE_STATE note_state=NOTE_OFF;
static int note_bufno= NIL;      // NIL or index of dma buffer
static int note_attack_bufcount;
static int note_attack_delta;
static int note_attack_bufs;
static int note_decay_bufs;
static int note_decay_bufcount;
static int note_decay_delta;
static int note_release_delta;
static int note_first_bufval;
static int note_last_bufval;
static int note_release_numerator;
static int note_release_denominator;

void note_init(void)
{
    note_freq = 0;
    note_state = NOTE_OFF;
    note_bufno = NIL;
    note_attack_bufcount=0;
    note_decay_bufcount=0;
    dma_init();
}

void note_start(void)
{
    if (wave_type_changed()) {
        wave_type_incr();
    }
    wave_reset(); 
    note_freq = touch_frequency();
    note_attack_bufs=envlp_get_attack_bufs();
    note_decay_bufs=envlp_get_decay_bufs();
    note_release_delta = envlp_get_release_delta();
    note_release_numerator = ENVLP_MAX - note_release_delta;
    note_release_denominator = ENVLP_MAX;
    // fill first two buffers
    note_attack();
    note_set_bufno(0);
    note_state_machine();
    note_fill_buf();
    note_set_bufno(1);
    note_state_machine();
    note_fill_buf();
    note_bufno=NIL;    
    dma_enable();
}

void note_end(void)
{
    dma_disable();
    note_state=NOTE_OFF;
    note_bufno=NIL;   
}

/* 
 * note_update 
 * update the frequency and attenuation factor for the 
 * current note.   If the current envelope first and last 
 * add up to less than 2, end the note. 
 * otherwise check to see if it is time to fill a dma buffer. 
 * if so fill the one specified by note_fillbuf. 
 */

void note_update(void)
{
    // execute note state machine
    note_state_machine();
    if(note_bufno!=NIL) {
        note_fill_buf();
    }

    // handle presence or absence of touch
    if (touch_debounce()) {
        wait_ms(10);  // wait 10 msec if touch present
        if (note_released() || !note_active()) {
            note_start();
        }
    } else {
        if (note_active()) {
            if (!note_released()) {
                note_release();
            }
        }
    }
}

void note_state_machine(void)
{
     // execute state machine that manages envelope

    switch (note_state) {
    case NOTE_ATTACK:
        if (note_attack_done()) {
            note_state = NOTE_DECAY;
        }
        note_freq = touch_frequency(); 
        break;

    case NOTE_DECAY:
        if(note_decay_done()) {
            note_state = NOTE_SUSTAIN;
        }
        note_freq = touch_frequency(); 
        break;

    case NOTE_SUSTAIN:
        note_freq = touch_frequency(); 
        break;

    case NOTE_RELEASE:
        if (note_release_done()) {
            note_end();
            break;
        }
        break;
    case NOTE_OFF:
    default:
        break;
    }
}

/*
 * note_fill_buf 
 *    Use the first and last buffer envelope values, and the
 *    wave values returned by wave_nextval() to compute the
 *    envelop-modified wave values, and then apply the changes
 *    necessary to output the values to the DAC data register.
 */
void note_fill_buf(void)
{
    int j,start_env,end_env,env_val,wave_val,buf_val,amplitude;
    int *bufptr;

    bufptr = dma_get_bufptr(note_bufno);
    start_env = note_first_bufval; 
    end_env = note_last_bufval;
    amplitude = touch_amplitude();  

    for (j=0;j<DMA_BUFSIZE;j++) {
            env_val=start_env+(end_env - start_env)*j/DMA_BUFSIZE;
            wave_val = wave_nextval(note_freq);
            buf_val = wave_val * env_val / ENVLP_MAX;
            buf_val=amplitude*buf_val/TOUCH_MAX_AMPLITUDE;
            bufptr[j]= DAC_POWER_MODE | ((buf_val << 6) & 0xFFC0);   
    }
            
    note_bufno = NIL;
    switch (note_state) {
    case NOTE_ATTACK:
        note_attack_bufcount++;
        break;
    case NOTE_DECAY:
        note_decay_bufcount++;
        break;
    case NOTE_SUSTAIN:
        note_first_bufval = note_last_bufval;
        break;
    case NOTE_RELEASE:
        note_first_bufval = note_last_bufval; 
        note_last_bufval =
            note_first_bufval * note_release_numerator 
            / note_release_denominator;
        break;
    case NOTE_OFF:
    default:
        ;
    }
}

/*
 * note_release 
 *    touch has been lifted, note begins to decay 
 */
void note_release(void)
{
    note_state = NOTE_RELEASE;
}
/*
 * note_released 
 *    return true if note has been released
 *    but not yet ended
 */
bool note_released(void)
{
    return (note_state == NOTE_RELEASE);
}
bool note_active(void)
{
    return (note_state < NOTE_OFF);
}
/*
 ** note_set_bufno 
 *     Set the index of the dma buffer to fill.
 * 
 */
void note_set_bufno(int bufno)
{
    note_bufno = bufno;
}

void note_attack(void)
{
    note_state = NOTE_ATTACK;
    note_attack_bufcount = 0;
    note_attack_delta = ENVLP_MAX / note_attack_bufs;
    note_first_bufval=note_attack_bufcount*note_attack_delta;
    note_last_bufval=note_first_bufval + note_attack_delta;
}

bool note_attack_done(void)
{
    if (note_attack_bufcount >= note_attack_bufs) {
        note_decay_bufcount = 0;
        note_decay_delta = (ENVLP_MAX >> 1) / note_decay_bufs;
        note_first_bufval = note_last_bufval;
        return true;
    } else {
        note_first_bufval=note_attack_bufcount*note_attack_delta; 
        note_last_bufval=note_first_bufval+note_attack_delta;
    }
    return false;
}

bool note_decay_done(void)
{
    if (note_decay_bufcount >= note_decay_bufs) {
        note_first_bufval = note_last_bufval;
        return true;
    } else {
        note_last_bufval=note_first_bufval - note_decay_delta;
        if (note_last_bufval < 0) {
            note_last_bufval=0;
        }
    }
    return false;
}

bool note_release_done(void)
{
    if (note_first_bufval < 25) return true;
    return false;
}