/*! @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 "Board_GLCD.h" #include "LPC17xx.h" #include "melody.h" #include "Welcome.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]; const char *cpcExpectedText; }; volatile static struct AudioState sAudioState; bool bPerformAudioStep() { bool bSuccess = true; sAudioState.highState = !sAudioState.highState; if (sAudioState.highState) { if (sAudioState.u16RemainingWaves > 0) { DAC_SetValue((u32Volume << 6)); } else { if (FREQ(*sAudioState.cpu32NextAction) == 0) { // GOTO sAudioState.highState = false; sAudioState.cpu32NextAction = cppu32JumpLabels[*sAudioState.cpu32NextAction]; bSuccess = false; } else if (FREQ(*sAudioState.cpu32NextAction) == 1) { // CALL sAudioState.highState = false; sAudioState.cpu32Stack[sAudioState.u32StackPointer] = sAudioState.cpu32NextAction + 1; sAudioState.cpu32NextAction = cppu32JumpLabels[WAVES(*sAudioState.cpu32NextAction)]; sAudioState.u32StackPointer++; bSuccess = false; } else if (FREQ(*sAudioState.cpu32NextAction) == 2) { // RETURN sAudioState.highState = false; sAudioState.u32StackPointer--; sAudioState.cpu32NextAction = sAudioState.cpu32Stack[sAudioState.u32StackPointer]; bSuccess = false; } else if (FREQ(*sAudioState.cpu32NextAction) == 3) { // PAUSE sAudioState.highState = false; LPC_TIM0->MR0 = WAVES(*sAudioState.cpu32NextAction) << 12; sAudioState.cpu32NextAction++; } else if (FREQ(*sAudioState.cpu32NextAction) == 4) { // TEXT sAudioState.highState = false; sAudioState.cpcExpectedText = cpcTexts[WAVES(*sAudioState.cpu32NextAction)]; sAudioState.cpu32NextAction++; bSuccess = false; } else { DAC_SetValue(512); 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++; } } } else { DAC_SetValue(0); sAudioState.u16RemainingWaves--; if (sAudioState.u16RemainingWaves == 0) { LPC_TIM0->MR0 = sAudioState.u32EndBreak; } } return bSuccess; } void vUpdateLyrics() { static const char *cpcCurrentText = 0; if (cpcCurrentText == 0) { cpcCurrentText = cpcTexts[0]; } const char *cpcExpectedText = sAudioState.cpcExpectedText; if (cpcCurrentText != cpcExpectedText) { GLCD_DrawString(0, 213, cpcExpectedText); cpcCurrentText = cpcExpectedText; } } void TIMER0_IRQHandler() { LPC_TIM0->IR = 0x1; // acknowledge interrupt while (!bPerformAudioStep()) { } } // Initializes the interrupt-driven audio output void vStartAudio() { sAudioState.highState = false; sAudioState.u16RemainingWaves = 0; sAudioState.cpu32NextAction = cpu32EntryPoint; sAudioState.u32StackPointer = 0; sAudioState.cpcExpectedText = cpcTexts[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 music }