188 lines
5.2 KiB
C
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);
|
|
}
|