/*! @file audio.c

  @brief   play sound
  @author  Lukas Fürderer
  @version V1.0
  @date    15.11.2020

This file contains the main app function and init.
*/

#include "audio.h"
#include "Board_DAC.h"
#include "LPC17xx.h"
#include "melody.h"

#define FREQ(x) ((x & 0xffff0000) >> 16)
#define WAVES(x) (x & 0xffff)

struct AudioState
{
    bool highState;
    uint16_t u16RemainingWaves;
    uint32_t u32EndBreak;
    const uint32_t *cpu32NextAction;
    uint32_t u32StackPointer;
    const uint32_t *cpu32Stack[10];
};

static struct AudioState sAudioState;

bool bPerformAudioStep()
{
    bool bSuccess = true;
    struct AudioState *psState = &sAudioState;
    psState = psState;

    sAudioState.highState = !sAudioState.highState;
    if (sAudioState.highState)
    {
        DAC_SetValue(512);
        if (sAudioState.u16RemainingWaves == 0)
        {
            LPC_TIM0->MR0 = sAudioState.u32EndBreak;
        }
    }
    else
    {
        DAC_SetValue(0);
        if (sAudioState.u16RemainingWaves > 0)
        {
            sAudioState.u16RemainingWaves--;
        }
        else
        {
            if (FREQ(*sAudioState.cpu32NextAction) == 0)
            {
                sAudioState.highState = true;
                sAudioState.cpu32NextAction = cppu32JumpLabels[*sAudioState.cpu32NextAction];
                bSuccess = false;
            }
            else if (FREQ(*sAudioState.cpu32NextAction) == 1)
            {
                sAudioState.highState = true;
                sAudioState.cpu32Stack[sAudioState.u32StackPointer] = sAudioState.cpu32NextAction + 1;
                sAudioState.cpu32NextAction = cppu32JumpLabels[WAVES(*sAudioState.cpu32NextAction)];
                sAudioState.u32StackPointer++;
                bSuccess = false;
            }
            else if (FREQ(*sAudioState.cpu32NextAction) == 2)
            {
                sAudioState.highState = true;
                sAudioState.u32StackPointer--;
                sAudioState.cpu32NextAction = sAudioState.cpu32Stack[sAudioState.u32StackPointer];
                bSuccess = false;
            }
            else
            {
                uint32_t u32Frequency = FREQ(*sAudioState.cpu32NextAction);
                LPC_TIM0->MR0 = u32Frequency * 2;
                uint32_t u32Waves = WAVES(*sAudioState.cpu32NextAction);
                sAudioState.u16RemainingWaves = u32Waves * 9 / 10 - 1;
                sAudioState.u32EndBreak = u32Waves * u32Frequency * 2 / 5;
                sAudioState.cpu32NextAction++;
            }
        }
    }

    return bSuccess;
}

void TIMER0_IRQHandler()
{
    LPC_TIM0->IR = 0x1; // acknowledge interrupt

    while (!bPerformAudioStep())
    {
    }
}

// Initializes the interrupt-driven audio output
void vStartAudio()
{
    sAudioState.highState = true;
    sAudioState.u16RemainingWaves = 0;
    sAudioState.cpu32NextAction = cpu32EntryPoint;
    sAudioState.u32StackPointer = 0;

    // Init timerinterrupt 0 (20 ms)
    LPC_SC->PCONP |= (1 << 2); // turn on
    LPC_SC->PCLKSEL0 |= (1 << 2); // select clock
    LPC_TIM0->MR0 = 100000 * 2 - 1;
    LPC_TIM0->MCR = 0x3;
    NVIC_SetPriority(TIMER0_IRQn, 1U); // priority
    NVIC_EnableIRQ(TIMER0_IRQn);
    LPC_TIM0->TCR = 1; // start
}
