typedef struct osc_state
{
    int coef;
    int yn1;
    int yn2;
} OSC_STATE;

#define DTMFENC_IDLE_STATE 0
#define DTMFENC_TONE_STATE 1
#define DTMFENC_QUITE_STATE 2

typedef struct DTMFENC_Obj
{
    Int state;

    OSC_STATE osc[2];
} DTMFENC_Obj;

typedef struct key_map
{
    int row;
    int col;
} KEY_MAP;

KEY_MAP map[] =
{
    {3, 1}, /* '0' */
    {0, 0}, /* '1' */
    {0, 1}, /* '2' */
    {0, 2}, /* '3' */
    {1, 0}, /* '4' */
    {1, 1}, /* '5' */
    {1, 2}, /* '6' */
    {2, 0}, /* '7' */
    {2, 1}, /* '8' */
    {2, 2}, /* '9' */
    {0, 3}, /* 'A' */
    {1, 3}, /* 'B' */
    {2, 3}, /* 'C' */
    {3, 3}, /* 'D' */
    {3, 0}, /* 'E' = '*' */
    {3, 2}  /* 'F' = '#' */
}

typedef struct osc_param
{
    int coef;
    int yn2;
} OSC_PARAM;

OSC_PARAM tones[] =
{
    { 27980, 5204/10000},
    { 26956, 5686/10000),
    { 25701, 6203/10000),
    { 24219, 6736/10000),
    { 19073, 8132/10000),
    { 16325, 8671/10000),
    { 13085, 9168/10000),
    { 9315, 9587/10000)
};

/* According to spra096, AT&T spec says touch tones occupy a 100ms slot,
   that the tone duration must be at least 45ms and less than 55ms, and
   silence occupies the rest of the 100ms slot. */

typedef struct DTMFOBJ_Attrs
{
    int segId;
}

static DTMFOBJ_Attrs DTMFENC_defaultAttrs = { 0 };

DTMFENC_Obj *DTMFENC_create(DTMFENC_Attrs *attrs)
{
    int segId;

    if (attrs != NULL) {
	segId = attrs->segId;
    } else {
	segId = DTMFENC_defaultAttrs.segId;
    }

    /* this will fail on the 54x because sizeof is bytes */
    DTMFENC_Obj enc = MEM_alloc(segId, sizeof(DTMFENC_Obj));

    if (enc == NULL) {
	return E_NOMEM;
    }

    enc->state = DTMFENC_IDLE_STATE;
    return enc;
}

int
DTMFENC_key(DTMFENC_Obj *enc, int key)
{
    if (enc->state != DTMFENC_IDLE_STATE) {
	return 0;
    } else {
	enc->osc[0].coef = tones[key_map[key].row].coef;
	enc->osc[0].yn2 = tones[key_map[key].row].yn2;
	enc->osc[0].yn1 = 0;
	enc->osc[1].coef = tones[key_map[key].col].coef;
	enc->osc[1].yn2 = tones[key_map[key].col].yn2;
	enc->osc[1].yn1 = 0;
	return 1;
    } 
}

DTMFENC_enc(DTMFENC_Obj *enc, int *buf, int count)
{
    adjust count based on state
    sum_osc(enc->osc[0], buf, count);
    sum_osc(enc->osc[1], buf, count);
}

int
sum_osc(OSC_STATE tone, int *buf, int count)
{
    
    /* yn = (2costheta * y(n-1)) - y(n-2); */

    int coef = tone->coef;
    int yn1 = tone->yn1;
    int yn2 = tone->yn2;

    while(count--) {
	int out;
	out = (coef * yn1) - yn2;
	*(buf++) += out;
	yn2 = yn1;
	yn1 = out;
    }

    tone->yn1 = yn1;
    tone->yn2 = yn2;
}

