/******************************************************************* 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 #include #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); }