188 lines
5.2 KiB
C

/*******************************************************************
File: waveplayer.c
Date: 29-September-2025
Author: Peter Spindler
Description: Functions to play a wave file. Requires functions from audio.c
********************************************************************/
#include "stm32f7xx_hal.h"
#include "audio.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "ff.h"
#include "fatfs.h"
#include "audio.h"
#include "gui.h"
#include "display.h"
#include <stdio.h>
#include <string.h>
#define BUTTON_PLUS 0
#define BUTTON_MINUS 1
#define BUTTON_PLAY 2
#define BUTTON_PAUSE 3
#define BUTTON_STOP 4
enum playerState {PAUSED, PLAYING, STOPPED} playerState = PLAYING;
FIL WaveFile;
#define AUDIO_BUFFER_SIZE_WORDS (1024*8)
uint16_t Audio_Buffer[AUDIO_BUFFER_SIZE_WORDS];
#define PLAYER_NOTIFY_HALF (1UL << 0)
#define PLAYER_NOTIFY_FULL (1UL << 1)
static TaskHandle_t PlayerTaskHandle = NULL;
SemaphoreHandle_t Semphr_FillBuffer = NULL;
uint8_t fillFlag = 0;
// helper to update volume value on display
uint8_t volume = 70;
void drawVolume(){
Display_Printf( 0, 40,
LCD_COLOR_WHITE, LCD_COLOR_BLACK,
&FontBig,
"%03d", volume
);
}
void Player_Start( const char *Filename ) {
FRESULT res;
printf("debug: Player_Start opening file...\n");
// FIXME: Currently is stuck / crashes here (when stopping, then playing again) ===================
if( (res=f_open(&WaveFile, Filename , FA_READ )) != FR_OK) {
printf("Error: Can't open audio file %s: %d\n", Filename, res);
return;
}
printf("debug: Player_start finished opening file. \n");
printf("Open wavefile %s: successful\n", Filename );
Audio_SetFrequency(22050);
// fill audio buffer completely
UINT bytesRead = 0;
res = f_read(&WaveFile, Audio_Buffer, sizeof(Audio_Buffer), &bytesRead);
if( res != FR_OK ) {
printf("Error: Can't read audio file %s: %d\n", Filename, res);
f_close(&WaveFile);
return;
}
Audio_Play( (uint8_t*) Audio_Buffer, sizeof(Audio_Buffer)/2 );
}
void Player_Stop( void ) {
Audio_Stop();
f_close(&WaveFile);
}
// Player_HalfTransfer_CallBack: called from DMA2_Stream4-ISR if first half of audio buffer has been transferred to SAI2
void Player_HalfTransfer_CallBack( void ) {
fillFlag = 0;
//printf("give 0\n");
xSemaphoreGiveFromISR(Semphr_FillBuffer, NULL);
}
// Player_CompleteTransfer_CallBack: called from DMA2_Stream4-ISR if second half of audio buffer has been transferred to SAI2
void Player_CompleteTransfer_CallBack( void ) {
fillFlag = 1;
//printf("give 1\n");
xSemaphoreGiveFromISR(Semphr_FillBuffer, NULL);
}
static void Player_Task( void *arguments ) {
UINT bytesRead = 0;
uint32_t notifyValue = 0;
drawVolume();
while(1) {
// 1. fill audio buffer if requested
if (xSemaphoreTake(Semphr_FillBuffer, pdMS_TO_TICKS(20))){
if (playerState != PLAYING) continue;
if (fillFlag == 0){ // half flag -> fill first half
if( f_read(&WaveFile, &Audio_Buffer[0], sizeof(Audio_Buffer)/2, &bytesRead) != FR_OK ) {
bytesRead = 0;
}
if( bytesRead < AUDIO_BUFFER_SIZE_WORDS/2 ) {
printf("error reading bytes, stopping playback\n");
Player_Stop();
playerState = STOPPED;
}
}
else if (fillFlag == 1){ // complete flag -> fill second half
bytesRead = 0;
if( f_read(&WaveFile, &Audio_Buffer[AUDIO_BUFFER_SIZE_WORDS/2], sizeof(Audio_Buffer)/2, &bytesRead) != FR_OK ) {
bytesRead = 0;
}
if( bytesRead < AUDIO_BUFFER_SIZE_WORDS/2 ) {
printf("error reading bytes, stopping playback\n");
Player_Stop();
playerState = STOPPED;
}
}
}
// 2. run player control (handle gui events)
//printf("running player control...\n");
uint8_t gui_msg = 0;
if (xQueueReceive(GUIQueue, &gui_msg, pdMS_TO_TICKS(0))){
printf("Received GUI event %d\n", gui_msg);
switch(gui_msg){
case BUTTON_PLUS:
if (++volume > 100) volume = 100;
drawVolume();
Audio_SetVolume(volume);
break;
case BUTTON_MINUS:
if (volume-- == 0) volume = 0;
drawVolume();
Audio_SetVolume(volume);
break;
case BUTTON_PLAY:
printf("Play\r\n");
if (playerState == STOPPED){
printf("was stopped, starting player from start\n");
// FATFS fs;
// printf("1. mounting filesystem...\n");
// f_mount(&fs, "/", 0);
Player_Start("audio.wav");
} else if (playerState == PAUSED){
printf("was paused, resuming...\n");
Audio_Resume();
}
playerState = PLAYING;
break;
case BUTTON_PAUSE:
printf("Pausing\r\n");
Audio_Pause();
playerState = PAUSED;
break;
case BUTTON_STOP:
printf("Stopping player\r\n");
Player_Stop();
playerState = STOPPED;
break;
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void Player_Init( void ) {
portENABLE_INTERRUPTS(); // Workaround to re-enable interrupts after FreeRTOS functions
// 70: initial volume, 44100: initial sampling frequency
if( Audio_Init(OUTPUT_DEVICE_AUTO,70,44100) != AUDIO_OK ) {
printf("Error: Can't init audio!\n");
}
Semphr_FillBuffer = xSemaphoreCreateBinary();
xTaskCreate(Player_Task,"Player", 512,NULL,0,&PlayerTaskHandle);
}