STM32F767 音乐播放器 SAI DMA单缓冲 可播放WAV、MP3、FLAC文件

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "delay.h"
#include "bsp_printf.h"
#include "bsp_key.h"
#include "string.h"
#include "bsp_sdram.h"
#include "bsp_malloc.h"
#include "bsp_sdmmc.h"
#include "ff.h"			/* Obtains integer types */
#include "bsp_exfuns.h"
#include "bsp_audioplay.h"
//#include "bsp_w25qxx.h"
//#include "bsp_ftl.h"
//#include "bsp_nand.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;

SD_HandleTypeDef hsd1;
DMA_HandleTypeDef hdma_sdmmc1_rx;
DMA_HandleTypeDef hdma_sdmmc1_tx;

UART_HandleTypeDef huart1;

SDRAM_HandleTypeDef hsdram1;

/* USER CODE BEGIN PV */

volatile uint8_t rx_done, tx_done;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_FMC_Init(void);
static void MX_DMA_Init(void);
static void MX_SDMMC1_SD_Init(void);
static void MX_SAI1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

static void Sdram_SendCommand(uint32_t CommandMode, uint32_t CommandTarget, uint32_t AutoRefreshNumber, uint32_t ModeRegisterDefinition)
{
	FMC_SDRAM_CommandTypeDef Command;
	
	Command.AutoRefreshNumber = AutoRefreshNumber;
	Command.CommandMode = CommandMode;
	Command.CommandTarget = CommandTarget;
	Command.ModeRegisterDefinition = ModeRegisterDefinition;
	
	HAL_SDRAM_SendCommand(&hsdram1, &Command, 0);
}

static void Sdram_Init_Sequence(void)
{
	uint32_t ModeRegisterDefinition;
	
	Sdram_SendCommand(FMC_SDRAM_CMD_CLK_ENABLE, FMC_SDRAM_CMD_TARGET_BANK1, 1, 0);//时钟配置使能
	
	delay_us(500);//至少延时200us
	
	Sdram_SendCommand(FMC_SDRAM_CMD_PALL, FMC_SDRAM_CMD_TARGET_BANK1, 1, 0);//对所有存储区预充电
	
	Sdram_SendCommand(FMC_SDRAM_CMD_AUTOREFRESH_MODE, FMC_SDRAM_CMD_TARGET_BANK1, 8, 0);//设置自刷新次数 
	
	//
	#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
	#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
	#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
	#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
	#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)
	#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)
	#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
	#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
	#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)
	#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
	#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)

	ModeRegisterDefinition=(uint32_t)SDRAM_MODEREG_BURST_LENGTH_1          |	//设置突发长度:1(可以是1/2/4/8)
              SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |	//设置突发类型:连续(可以是连续/交错)
              SDRAM_MODEREG_CAS_LATENCY_3           |	//设置CAS值:3(可以是2/3)
              SDRAM_MODEREG_OPERATING_MODE_STANDARD |   //设置操作模式:0,标准模式
              SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;     //设置突发写模式:1,单点访问

	Sdram_SendCommand(FMC_SDRAM_CMD_LOAD_MODE, FMC_SDRAM_CMD_TARGET_BANK1, 1, ModeRegisterDefinition);
	
	HAL_SDRAM_ProgramRefreshRate(&hsdram1, 823);
}

//通过串口打印SD卡相关信息
void show_sdcard_info(void)
{
	HAL_SD_CardCIDTypeDef cid;
	
	switch(hsd1.SdCard.CardVersion)
	{
		case CARD_V1_X:printf("Card Version:CARD_V1_X\r\n");break;
		case CARD_V2_X:printf("Card Version:CARD_V2_X\r\n");break;
		
	}	
	switch(hsd1.SdCard.CardType)
	{
		case CARD_SDSC:printf("Card Type:CARD_SDSC\r\n");break;
		case CARD_SDHC_SDXC:printf("Card Type:CARD_SDHC_SDXC\r\n");break;
		case CARD_SECURED:printf("Card Type:CARD_SECURED\r\n");break;
	}
	
	if(HAL_OK != HAL_SD_GetCardCID(&hsd1, &cid))
	{
		Error_Handler();
	}
	
  printf("Card ManufacturerID:%d\r\n",cid.ManufacturerID);	//制造商ID
 	printf("Card RCA:%d\r\n",hsd1.SdCard.RelCardAdd );								//卡相对地址
	printf("Card Capacity:%d MB\r\n",(uint32_t)(((uint64_t)hsd1.SdCard.BlockNbr*hsd1.SdCard.BlockSize)>>20));	//显示容量
 	printf("Card BlockSize:%d\r\n\r\n",hsd1.SdCard.BlockSize);			//显示块大小
	printf("Card LogBlockNbr:%d\r\n\r\n",hsd1.SdCard.LogBlockNbr);
	printf("Card LogBlockSize:%d\r\n\r\n",hsd1.SdCard.LogBlockSize);
}


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
	
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_FMC_Init();
  MX_DMA_Init();
  MX_SDMMC1_SD_Init();
  MX_SAI1_Init();
  /* USER CODE BEGIN 2 */
	
	delay_init(216);
	delay_ms(5000);
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
	
	Sdram_Init_Sequence(); 
	my_mem_init(SRAMIN);            //初始化内部内存池
	my_mem_init(SRAMEX);            //初始化外部SDRAM内存池
	
	exfuns_init();		            //为fatfs相关变量申请内存   
	f_mount(fs[0],"0:",1);          //挂载SD卡  

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		audio_play();
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Configure LSE Drive Capability
  */
  HAL_PWR_EnableBkUpAccess();
  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 432;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 9;
  RCC_OscInitStruct.PLL.PLLR = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Activate the Over-Drive mode
  */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_SAI1
                              |RCC_PERIPHCLK_SDMMC1|RCC_PERIPHCLK_CLK48;
  PeriphClkInitStruct.PLLSAI.PLLSAIN = 288;
  PeriphClkInitStruct.PLLSAI.PLLSAIR = 4;
  PeriphClkInitStruct.PLLSAI.PLLSAIQ = 4;
  PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV2;
  PeriphClkInitStruct.PLLSAIDivQ = 1;
  PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_8;
  PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI;
  PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLL;
  PeriphClkInitStruct.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_CLK48;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Enables the Clock Security System
  */
  HAL_RCC_EnableCSS();
}

/**
  * @brief SAI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SAI1_Init(void)
{

  /* USER CODE BEGIN SAI1_Init 0 */

  /* USER CODE END SAI1_Init 0 */

  /* USER CODE BEGIN SAI1_Init 1 */

  /* USER CODE END SAI1_Init 1 */
  hsai_BlockA1.Instance = SAI1_Block_A;
  hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
  hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
  hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
  hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
  hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_192K;
  hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
  hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
  hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
  if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, 2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SAI1_Init 2 */

  /* USER CODE END SAI1_Init 2 */

}

/**
  * @brief SDMMC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SDMMC1_SD_Init(void)
{

  /* USER CODE BEGIN SDMMC1_Init 0 */

  /* USER CODE END SDMMC1_Init 0 */

  /* USER CODE BEGIN SDMMC1_Init 1 */

  /* USER CODE END SDMMC1_Init 1 */
  hsd1.Instance = SDMMC1;
  hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
  hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
  hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
  hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
  hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd1.Init.ClockDiv = 0;
  if (HAL_SD_Init(&hsd1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SDMMC1_Init 2 */

  /* USER CODE END SDMMC1_Init 2 */

}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

}

/* FMC initialization function */
static void MX_FMC_Init(void)
{

  /* USER CODE BEGIN FMC_Init 0 */

  /* USER CODE END FMC_Init 0 */

  FMC_SDRAM_TimingTypeDef SdramTiming = {0};

  /* USER CODE BEGIN FMC_Init 1 */

  /* USER CODE END FMC_Init 1 */

  /** Perform the SDRAM1 memory initialization sequence
  */
  hsdram1.Instance = FMC_SDRAM_DEVICE;
  /* hsdram1.Init */
  hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
  hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
  hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
  hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
  hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
  hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 8;
  SdramTiming.SelfRefreshTime = 4;
  SdramTiming.RowCycleDelay = 7;
  SdramTiming.WriteRecoveryTime = 3;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

  if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FMC_Init 2 */

  /* USER CODE END FMC_Init 2 */
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_5, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PA0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PH2 PH3 */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

  /*Configure GPIO pins : PB0 PB5 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pin : PB1 */
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32f7xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

//extern SDRAM_HandleTypeDef hsdram1;
extern SD_HandleTypeDef hsd1;
//extern QSPI_HandleTypeDef hqspi;
//extern NAND_HandleTypeDef hnand1;
extern SAI_HandleTypeDef hsai_BlockA1;
extern DMA_HandleTypeDef hdma_sai1_a;

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

extern volatile uint8_t rx_done, tx_done;

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */

定义一些常用的数据类型短关键字 
//typedef int32_t  s32;
//typedef int16_t s16;
//typedef int8_t  s8;

//typedef const int32_t sc32;  
//typedef const int16_t sc16;  
//typedef const int8_t sc8;  

//typedef __IO int32_t  vs32;
//typedef __IO int16_t  vs16;
//typedef __IO int8_t   vs8;

//typedef __I int32_t vsc32;  
//typedef __I int16_t vsc16; 
//typedef __I int8_t vsc8;   

//typedef uint32_t  u32;
//typedef uint16_t u16;
//typedef uint8_t  u8;

//typedef const uint32_t uc32;  
//typedef const uint16_t uc16;  
//typedef const uint8_t uc8; 

//typedef __IO uint32_t  vu32;
//typedef __IO uint16_t vu16;
//typedef __IO uint8_t  vu8;

//typedef __I uint32_t vuc32;  
//typedef __I uint16_t vuc16; 
//typedef __I uint8_t vuc8; 

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f7xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f7xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "fifo.h"
#include "bsp_wavplay.h" 
#include "bsp_mp3play.h"
#include "bsp_flacplay.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_sai1_a;
extern DMA_HandleTypeDef hdma_sdmmc1_rx;
extern DMA_HandleTypeDef hdma_sdmmc1_tx;
extern SD_HandleTypeDef hsd1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M7 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  HAL_RCC_NMI_IRQHandler();
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Pre-fetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F7xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f7xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles SDMMC1 global interrupt.
  */
void SDMMC1_IRQHandler(void)
{
  /* USER CODE BEGIN SDMMC1_IRQn 0 */

  /* USER CODE END SDMMC1_IRQn 0 */
  HAL_SD_IRQHandler(&hsd1);
  /* USER CODE BEGIN SDMMC1_IRQn 1 */

  /* USER CODE END SDMMC1_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream1 global interrupt.
  */
void DMA2_Stream1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */

  /* USER CODE END DMA2_Stream1_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sai1_a);
  /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */

  /* USER CODE END DMA2_Stream1_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream3 global interrupt.
  */
void DMA2_Stream3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */

  /* USER CODE END DMA2_Stream3_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sdmmc1_rx);
  /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */

  /* USER CODE END DMA2_Stream3_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream6 global interrupt.
  */
void DMA2_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */

  /* USER CODE END DMA2_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_sdmmc1_tx);
  /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */

  /* USER CODE END DMA2_Stream6_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/**
  * @brief Rx Transfer completed callbacks
  * @param hsd: Pointer SD handle
  * @retval None
  */
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
	rx_done = 1;
}

/**
  * @brief Tx Transfer completed callbacks
  * @param hsd: Pointer to SD handle
  * @retval None
  */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);
	tx_done = 1;
}

/**
  * @brief SD error callbacks
  * @param hsd: Pointer SD handle
  * @retval None
  */
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd)
{
	HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}

/**
  * @brief Tx Transfer completed callback.
  * @param  hsai pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
	wavtransferend = 1;
	mp3transferend = 1;
	flactransferend = 1; 
}


/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file         stm32f7xx_hal_msp.c
  * @brief        This file provides code for the MSP Initialization
  *               and de-Initialization codes.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */
extern DMA_HandleTypeDef hdma_sdmmc1_rx;

extern DMA_HandleTypeDef hdma_sdmmc1_tx;

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN Define */

/* USER CODE END Define */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN Macro */

/* USER CODE END Macro */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* External functions --------------------------------------------------------*/
/* USER CODE BEGIN ExternalFunctions */

/* USER CODE END ExternalFunctions */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */
/**
  * Initializes the Global MSP.
  */
void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_RCC_SYSCFG_CLK_ENABLE();

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

/**
* @brief SD MSP Initialization
* This function configures the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hsd->Instance==SDMMC1)
  {
  /* USER CODE BEGIN SDMMC1_MspInit 0 */

  /* USER CODE END SDMMC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_SDMMC1_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDMMC1 GPIO Configuration
    PC8     ------> SDMMC1_D0
    PC9     ------> SDMMC1_D1
    PC10     ------> SDMMC1_D2
    PC11     ------> SDMMC1_D3
    PC12     ------> SDMMC1_CK
    PD2     ------> SDMMC1_CMD
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

    /* SDMMC1 DMA Init */
    /* SDMMC1_RX Init */
    hdma_sdmmc1_rx.Instance = DMA2_Stream3;
    hdma_sdmmc1_rx.Init.Channel = DMA_CHANNEL_4;
    hdma_sdmmc1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_sdmmc1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sdmmc1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sdmmc1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_sdmmc1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_sdmmc1_rx.Init.Mode = DMA_PFCTRL;
    hdma_sdmmc1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_sdmmc1_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_sdmmc1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_sdmmc1_rx.Init.MemBurst = DMA_MBURST_INC4;
    hdma_sdmmc1_rx.Init.PeriphBurst = DMA_PBURST_INC4;
    if (HAL_DMA_Init(&hdma_sdmmc1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hsd,hdmarx,hdma_sdmmc1_rx);

    /* SDMMC1_TX Init */
    hdma_sdmmc1_tx.Instance = DMA2_Stream6;
    hdma_sdmmc1_tx.Init.Channel = DMA_CHANNEL_4;
    hdma_sdmmc1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_sdmmc1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_sdmmc1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_sdmmc1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_sdmmc1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_sdmmc1_tx.Init.Mode = DMA_PFCTRL;
    hdma_sdmmc1_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_sdmmc1_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_sdmmc1_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_sdmmc1_tx.Init.MemBurst = DMA_MBURST_INC4;
    hdma_sdmmc1_tx.Init.PeriphBurst = DMA_PBURST_INC4;
    if (HAL_DMA_Init(&hdma_sdmmc1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(hsd,hdmatx,hdma_sdmmc1_tx);

    /* SDMMC1 interrupt Init */
    HAL_NVIC_SetPriority(SDMMC1_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
  /* USER CODE BEGIN SDMMC1_MspInit 1 */

  /* USER CODE END SDMMC1_MspInit 1 */
  }

}

/**
* @brief SD MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd)
{
  if(hsd->Instance==SDMMC1)
  {
  /* USER CODE BEGIN SDMMC1_MspDeInit 0 */

  /* USER CODE END SDMMC1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SDMMC1_CLK_DISABLE();

    /**SDMMC1 GPIO Configuration
    PC8     ------> SDMMC1_D0
    PC9     ------> SDMMC1_D1
    PC10     ------> SDMMC1_D2
    PC11     ------> SDMMC1_D3
    PC12     ------> SDMMC1_CK
    PD2     ------> SDMMC1_CMD
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12);

    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);

    /* SDMMC1 DMA DeInit */
    HAL_DMA_DeInit(hsd->hdmarx);
    HAL_DMA_DeInit(hsd->hdmatx);

    /* SDMMC1 interrupt DeInit */
    HAL_NVIC_DisableIRQ(SDMMC1_IRQn);
  /* USER CODE BEGIN SDMMC1_MspDeInit 1 */

  /* USER CODE END SDMMC1_MspDeInit 1 */
  }

}

/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }

}

/**
* @brief UART MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }

}

static uint32_t FMC_Initialized = 0;

static void HAL_FMC_MspInit(void){
  /* USER CODE BEGIN FMC_MspInit 0 */

  /* USER CODE END FMC_MspInit 0 */
  GPIO_InitTypeDef GPIO_InitStruct ={0};
  if (FMC_Initialized) {
    return;
  }
  FMC_Initialized = 1;

  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_ENABLE();

  /** FMC GPIO Configuration
  PF0   ------> FMC_A0
  PF1   ------> FMC_A1
  PF2   ------> FMC_A2
  PF3   ------> FMC_A3
  PF4   ------> FMC_A4
  PF5   ------> FMC_A5
  PC0   ------> FMC_SDNWE
  PC2   ------> FMC_SDNE0
  PC3   ------> FMC_SDCKE0
  PF11   ------> FMC_SDNRAS
  PF12   ------> FMC_A6
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PF15   ------> FMC_A9
  PG0   ------> FMC_A10
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
  PG2   ------> FMC_A12
  PG4   ------> FMC_BA0
  PG5   ------> FMC_BA1
  PG8   ------> FMC_SDCLK
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PG15   ------> FMC_SDNCAS
  PE0   ------> FMC_NBL0
  PE1   ------> FMC_NBL1
  */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12
                          |GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4
                          |GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN FMC_MspInit 1 */

  /* USER CODE END FMC_MspInit 1 */
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* hsdram){
  /* USER CODE BEGIN SDRAM_MspInit 0 */

  /* USER CODE END SDRAM_MspInit 0 */
  HAL_FMC_MspInit();
  /* USER CODE BEGIN SDRAM_MspInit 1 */

  /* USER CODE END SDRAM_MspInit 1 */
}

static uint32_t FMC_DeInitialized = 0;

static void HAL_FMC_MspDeInit(void){
  /* USER CODE BEGIN FMC_MspDeInit 0 */

  /* USER CODE END FMC_MspDeInit 0 */
  if (FMC_DeInitialized) {
    return;
  }
  FMC_DeInitialized = 1;
  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_DISABLE();

  /** FMC GPIO Configuration
  PF0   ------> FMC_A0
  PF1   ------> FMC_A1
  PF2   ------> FMC_A2
  PF3   ------> FMC_A3
  PF4   ------> FMC_A4
  PF5   ------> FMC_A5
  PC0   ------> FMC_SDNWE
  PC2   ------> FMC_SDNE0
  PC3   ------> FMC_SDCKE0
  PF11   ------> FMC_SDNRAS
  PF12   ------> FMC_A6
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PF15   ------> FMC_A9
  PG0   ------> FMC_A10
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
  PG2   ------> FMC_A12
  PG4   ------> FMC_BA0
  PG5   ------> FMC_BA1
  PG8   ------> FMC_SDCLK
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PG15   ------> FMC_SDNCAS
  PE0   ------> FMC_NBL0
  PE1   ------> FMC_NBL1
  */
  HAL_GPIO_DeInit(GPIOF, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12
                          |GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);

  HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3);

  HAL_GPIO_DeInit(GPIOG, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4
                          |GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15);

  HAL_GPIO_DeInit(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1);

  HAL_GPIO_DeInit(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1);

  /* USER CODE BEGIN FMC_MspDeInit 1 */

  /* USER CODE END FMC_MspDeInit 1 */
}

void HAL_SDRAM_MspDeInit(SDRAM_HandleTypeDef* hsdram){
  /* USER CODE BEGIN SDRAM_MspDeInit 0 */

  /* USER CODE END SDRAM_MspDeInit 0 */
  HAL_FMC_MspDeInit();
  /* USER CODE BEGIN SDRAM_MspDeInit 1 */

  /* USER CODE END SDRAM_MspDeInit 1 */
}

//extern DMA_HandleTypeDef hdma_sai1_a;

//static uint32_t SAI1_client =0;

//void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
//{

//  GPIO_InitTypeDef GPIO_InitStruct;
///* SAI1 */
//    if(hsai->Instance==SAI1_Block_A)
//    {
//    /* Peripheral clock enable */
//    if (SAI1_client == 0)
//    {
//       __HAL_RCC_SAI1_CLK_ENABLE();
//    }
//    SAI1_client ++;

//    /**SAI1_A_Block_A GPIO Configuration
//    PE2     ------> SAI1_MCLK_A
//    PE4     ------> SAI1_FS_A
//    PE5     ------> SAI1_SCK_A
//    PE6     ------> SAI1_SD_A
//    */
//    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
//    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
//    GPIO_InitStruct.Pull = GPIO_NOPULL;
//    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
//    GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;
//    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

//      /* Peripheral DMA init*/

//    hdma_sai1_a.Instance = DMA2_Stream1;
//    hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
//    hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
//    hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
//    hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
//    hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
//    hdma_sai1_a.Init.Mode = DMA_NORMAL;
//    hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
//    hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
//    hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
//    hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
//    hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
//    if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
//    {
//      Error_Handler();
//    }

//    /* Several peripheral DMA handle pointers point to the same DMA handle.
//     Be aware that there is only one stream to perform all the requested DMAs. */
//    __HAL_LINKDMA(hsai,hdmarx,hdma_sai1_a);

//    __HAL_LINKDMA(hsai,hdmatx,hdma_sai1_a);

//    }
//}

//void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
//{
///* SAI1 */
//    if(hsai->Instance==SAI1_Block_A)
//    {
//    SAI1_client --;
//    if (SAI1_client == 0)
//      {
//      /* Peripheral clock disable */
//       __HAL_RCC_SAI1_CLK_DISABLE();
//      }

//    /**SAI1_A_Block_A GPIO Configuration
//    PE2     ------> SAI1_MCLK_A
//    PE4     ------> SAI1_FS_A
//    PE5     ------> SAI1_SCK_A
//    PE6     ------> SAI1_SD_A
//    */
//    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);

//    /* SAI1 DMA Deinit */
//    HAL_DMA_DeInit(hsai->hdmarx);
//    HAL_DMA_DeInit(hsai->hdmatx);
//    }
//}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

#include "bsp_wm8978.h"
#include "myiic.h"
#include "delay.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//WM8978驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/29
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
// 	

//WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存
//因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值
//写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值.
//注意:WM8978的寄存器值是9位的,所以要用uint16_t来存储. 
static uint16_t WM8978_REGVAL_TBL[58]=
{
	0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000,
	0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF,
	0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000,
	0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,
	0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9,
	0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100,
	0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039,
	0X0001,0X0001
}; 

//WM8978初始化
//返回值:0,初始化正常
//    其他,错误代码
uint8_t WM8978_Init(void)
{ 
	uint8_t res;
	IIC_Init();                 //初始化IIC接口
	res=WM8978_Write_Reg(0,0);	//软复位WM8978
	if(res)return 1;			//发送指令失败,WM8978异常
	//以下为通用设置
	WM8978_Write_Reg(1,0X1B);	//R1,MICEN设置为1(MIC使能),BIASEN设置为1(模拟器工作),VMIDSEL[1:0]设置为:11(5K)
	WM8978_Write_Reg(2,0X1B0);	//R2,ROUT1,LOUT1输出使能(耳机可以工作),BOOSTENR,BOOSTENL使能
	WM8978_Write_Reg(3,0X6C);	//R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX使能	
	WM8978_Write_Reg(6,0);		//R6,MCLK由外部提供
	WM8978_Write_Reg(43,1<<4);	//R43,INVROUT2反向,驱动喇叭
	WM8978_Write_Reg(47,1<<8);	//R47设置,PGABOOSTL,左通道MIC获得20倍增益
	WM8978_Write_Reg(48,1<<8);	//R48设置,PGABOOSTR,右通道MIC获得20倍增益
	WM8978_Write_Reg(49,1<<1);	//R49,TSDEN,开启过热保护 
	WM8978_Write_Reg(49,1<<2);	//R49,SPEAKER BOOST,1.5x 
	WM8978_Write_Reg(10,1<<3);	//R10,SOFTMUTE关闭,128x采样,最佳SNR 
	WM8978_Write_Reg(14,1<<3);	//R14,ADC 128x采样率
	return 0;
} 
//WM8978写寄存器
//reg:寄存器地址
//val:要写入寄存器的值 
//返回值:0,成功;
//    其他,错误代码
uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val)
{ 
	IIC_Start(); 
	IIC_Send_Byte((WM8978_ADDR<<1)|0);//发送器件地址+写命令	 
	if(IIC_Wait_Ack())return 1;	//等待应答(成功?/失败?) 
    IIC_Send_Byte((reg<<1)|((val>>8)&0X01));//写寄存器地址+数据的最高位
	if(IIC_Wait_Ack())return 2;	//等待应答(成功?/失败?) 
	IIC_Send_Byte(val&0XFF);	//发送数据
	if(IIC_Wait_Ack())return 3;	//等待应答(成功?/失败?) 
    IIC_Stop();
	WM8978_REGVAL_TBL[reg]=val;	//保存寄存器值到本地
	return 0;	
}  
//WM8978读寄存器
//就是读取本地寄存器值缓冲区内的对应值
//reg:寄存器地址 
//返回值:寄存器值
uint16_t WM8978_Read_Reg(uint8_t reg)
{  
	return WM8978_REGVAL_TBL[reg];	
} 
//WM8978 DAC/ADC配置
//adcen:adc使能(1)/关闭(0)
//dacen:dac使能(1)/关闭(0)
void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen)
{
	uint16_t regval;
	regval=WM8978_Read_Reg(3);	//读取R3
	if(dacen)regval|=3<<0;		//R3最低2个位设置为1,开启DACR&DACL
	else regval&=~(3<<0);		//R3最低2个位清零,关闭DACR&DACL.
	WM8978_Write_Reg(3,regval);	//设置R3
	regval=WM8978_Read_Reg(2);	//读取R2
	if(adcen)regval|=3<<0;		//R2最低2个位设置为1,开启ADCR&ADCL
	else regval&=~(3<<0);		//R2最低2个位清零,关闭ADCR&ADCL.
	WM8978_Write_Reg(2,regval);	//设置R2	
}
//WM8978 输入通道配置 
//micen:MIC开启(1)/关闭(0)
//lineinen:Line In开启(1)/关闭(0)
//auxen:aux开启(1)/关闭(0) 
void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen)
{
	uint16_t regval;  
	regval=WM8978_Read_Reg(2);	//读取R2
	if(micen)regval|=3<<2;		//开启INPPGAENR,INPPGAENL(MIC的PGA放大)
	else regval&=~(3<<2);		//关闭INPPGAENR,INPPGAENL.
 	WM8978_Write_Reg(2,regval);	//设置R2 
	
	regval=WM8978_Read_Reg(44);	//读取R44
	if(micen)regval|=3<<4|3<<0;	//开启LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
	else regval&=~(3<<4|3<<0);	//关闭LIN2INPPGA,LIP2INPGA,RIN2INPPGA,RIP2INPGA.
	WM8978_Write_Reg(44,regval);//设置R44
	
	if(lineinen)WM8978_LINEIN_Gain(5);//LINE IN 0dB增益
	else WM8978_LINEIN_Gain(0);	//关闭LINE IN
	if(auxen)WM8978_AUX_Gain(7);//AUX 6dB增益
	else WM8978_AUX_Gain(0);	//关闭AUX输入  
}
//WM8978 输出配置 
//dacen:DAC输出(放音)开启(1)/关闭(0)
//bpsen:Bypass输出(录音,包括MIC,LINE IN,AUX等)开启(1)/关闭(0) 
void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen)
{
	uint16_t regval=0;
	if(dacen)regval|=1<<0;	//DAC输出使能
	if(bpsen)
	{
		regval|=1<<1;		//BYPASS使能
		regval|=5<<2;		//0dB增益
	} 
	WM8978_Write_Reg(50,regval);//R50设置
	WM8978_Write_Reg(51,regval);//R51设置 
}
//WM8978 MIC增益设置(不包括BOOST的20dB,MIC-->ADC输入部分的增益)
//gain:0~63,对应-12dB~35.25dB,0.75dB/Step
void WM8978_MIC_Gain(uint8_t gain)
{
	gain&=0X3F;
	WM8978_Write_Reg(45,gain);		//R45,左通道PGA设置 
	WM8978_Write_Reg(46,gain|1<<8);	//R46,右通道PGA设置
}
//WM8978 L2/R2(也就是Line In)增益设置(L2/R2-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void WM8978_LINEIN_Gain(uint8_t gain)
{
	uint16_t regval;
	gain&=0X07;
	regval=WM8978_Read_Reg(47);	//读取R47
	regval&=~(7<<4);			//清除原来的设置 
 	WM8978_Write_Reg(47,regval|gain<<4);//设置R47
	regval=WM8978_Read_Reg(48);	//读取R48
	regval&=~(7<<4);			//清除原来的设置 
 	WM8978_Write_Reg(48,regval|gain<<4);//设置R48
} 
//WM8978 AUXR,AUXL(PWM音频部分)增益设置(AUXR/L-->ADC输入部分的增益)
//gain:0~7,0表示通道禁止,1~7,对应-12dB~6dB,3dB/Step
void WM8978_AUX_Gain(uint8_t gain)
{
	uint16_t regval;
	gain&=0X07;
	regval=WM8978_Read_Reg(47);	//读取R47
	regval&=~(7<<0);			//清除原来的设置 
 	WM8978_Write_Reg(47,regval|gain<<0);//设置R47
	regval=WM8978_Read_Reg(48);	//读取R48
	regval&=~(7<<0);			//清除原来的设置 
 	WM8978_Write_Reg(48,regval|gain<<0);//设置R48
}  
//设置I2S工作模式
//fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP;
//len:0,16位;1,20位;2,24位;3,32位;  
void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len)
{
	fmt&=0X03;
	len&=0X03;//限定范围
	WM8978_Write_Reg(4,(fmt<<3)|(len<<5));	//R4,WM8978工作模式设置	
}	

//设置耳机左右声道音量
//voll:左声道音量(0~63)
//volr:右声道音量(0~63)
void WM8978_HPvol_Set(uint8_t voll,uint8_t volr)
{
	voll&=0X3F;
	volr&=0X3F;//限定范围
	if(voll==0)voll|=1<<6;//音量为0时,直接mute
	if(volr==0)volr|=1<<6;//音量为0时,直接mute 
	WM8978_Write_Reg(52,voll);			//R52,耳机左声道音量设置
	WM8978_Write_Reg(53,volr|(1<<8));	//R53,耳机右声道音量设置,同步更新(HPVU=1)
}
//设置喇叭音量
//voll:左声道音量(0~63) 
void WM8978_SPKvol_Set(uint8_t volx)
{ 
	volx&=0X3F;//限定范围
	if(volx==0)volx|=1<<6;//音量为0时,直接mute 
 	WM8978_Write_Reg(54,volx);			//R54,喇叭左声道音量设置
	WM8978_Write_Reg(55,volx|(1<<8));	//R55,喇叭右声道音量设置,同步更新(SPKVU=1)	
}
//设置3D环绕声
//depth:0~15(3D强度,0最弱,15最强)
void WM8978_3D_Set(uint8_t depth)
{ 
	depth&=0XF;//限定范围 
 	WM8978_Write_Reg(41,depth);	//R41,3D环绕设置 	
}
//设置EQ/3D作用方向
//dir:0,在ADC起作用
//    1,在DAC起作用(默认)
void WM8978_EQ_3D_Dir(uint8_t dir)
{
	uint16_t regval; 
	regval=WM8978_Read_Reg(0X12);
	if(dir)regval|=1<<8;
	else regval&=~(1<<8); 
 	WM8978_Write_Reg(18,regval);//R18,EQ1的第9位控制EQ/3D方向
}

//设置EQ1
//cfreq:截止频率,0~3,分别对应:80/105/135/175Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain)
{ 
	uint16_t regval;
	cfreq&=0X3;//限定范围 
	if(gain>24)gain=24;
	gain=24-gain;
	regval=WM8978_Read_Reg(18);
	regval&=0X100;
	regval|=cfreq<<5;	//设置截止频率 
	regval|=gain;		//设置增益	
 	WM8978_Write_Reg(18,regval);//R18,EQ1设置 	
}
//设置EQ2
//cfreq:中心频率,0~3,分别对应:230/300/385/500Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain)
{ 
	uint16_t regval=0;
	cfreq&=0X3;//限定范围 
	if(gain>24)gain=24;
	gain=24-gain; 
	regval|=cfreq<<5;	//设置截止频率 
	regval|=gain;		//设置增益	
 	WM8978_Write_Reg(19,regval);//R19,EQ2设置 	
}
//设置EQ3
//cfreq:中心频率,0~3,分别对应:650/850/1100/1400Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain)
{ 
	uint16_t regval=0;
	cfreq&=0X3;//限定范围 
	if(gain>24)gain=24;
	gain=24-gain; 
	regval|=cfreq<<5;	//设置截止频率 
	regval|=gain;		//设置增益	
 	WM8978_Write_Reg(20,regval);//R20,EQ3设置 	
}
//设置EQ4
//cfreq:中心频率,0~3,分别对应:1800/2400/3200/4100Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain)
{ 
	uint16_t regval=0;
	cfreq&=0X3;//限定范围 
	if(gain>24)gain=24;
	gain=24-gain; 
	regval|=cfreq<<5;	//设置截止频率 
	regval|=gain;		//设置增益	
 	WM8978_Write_Reg(21,regval);//R21,EQ4设置 	
}
//设置EQ5
//cfreq:中心频率,0~3,分别对应:5300/6900/9000/11700Hz
//gain:增益,0~24,对应-12~+12dB
void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain)
{ 
	uint16_t regval=0;
	cfreq&=0X3;//限定范围 
	if(gain>24)gain=24;
	gain=24-gain; 
	regval|=cfreq<<5;	//设置截止频率 
	regval|=gain;		//设置增益	
 	WM8978_Write_Reg(22,regval);//R22,EQ5设置 	
}

#ifndef __BSP_WM8978_H
#define __BSP_WM8978_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//WM8978驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/29
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
// 	

#define WM8978_ADDR     0X1A	//WM8978的器件地址,固定为0X1A 
 
#define EQ1_80Hz		0X00
#define EQ1_105Hz		0X01
#define EQ1_135Hz		0X02
#define EQ1_175Hz		0X03

#define EQ2_230Hz		0X00
#define EQ2_300Hz		0X01
#define EQ2_385Hz		0X02
#define EQ2_500Hz		0X03

#define EQ3_650Hz		0X00
#define EQ3_850Hz		0X01
#define EQ3_1100Hz		0X02
#define EQ3_14000Hz		0X03

#define EQ4_1800Hz		0X00
#define EQ4_2400Hz		0X01
#define EQ4_3200Hz		0X02
#define EQ4_4100Hz		0X03

#define EQ5_5300Hz		0X00
#define EQ5_6900Hz		0X01
#define EQ5_9000Hz		0X02
#define EQ5_11700Hz		0X03
 
uint8_t WM8978_Init(void); 
void WM8978_ADDA_Cfg(uint8_t dacen,uint8_t adcen);
void WM8978_Input_Cfg(uint8_t micen,uint8_t lineinen,uint8_t auxen);
void WM8978_Output_Cfg(uint8_t dacen,uint8_t bpsen);
void WM8978_MIC_Gain(uint8_t gain);
void WM8978_LINEIN_Gain(uint8_t gain);
void WM8978_AUX_Gain(uint8_t gain);
uint8_t WM8978_Write_Reg(uint8_t reg,uint16_t val); 
uint16_t WM8978_Read_Reg(uint8_t reg);
void WM8978_HPvol_Set(uint8_t voll,uint8_t volr);
void WM8978_SPKvol_Set(uint8_t volx);
void WM8978_I2S_Cfg(uint8_t fmt,uint8_t len);
void WM8978_3D_Set(uint8_t depth);
void WM8978_EQ_3D_Dir(uint8_t dir); 
void WM8978_EQ1_Set(uint8_t cfreq,uint8_t gain); 
void WM8978_EQ2_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ3_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ4_Set(uint8_t cfreq,uint8_t gain);
void WM8978_EQ5_Set(uint8_t cfreq,uint8_t gain);

#ifdef __cplusplus
}
#endif

#endif /* __BSP_WM8978_H */

#include "bsp_audioplay.h"
#include "ff.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
#include "bsp_wm8978.h"
#include "bsp_sai.h"
#include "delay.h"
#include "bsp_key.h"
#include "bsp_exfuns.h"  
//#include "text.h"
#include "string.h"  
#include "bsp_wavplay.h"  
#include "bsp_mp3play.h"
#include "bsp_flacplay.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//音乐播放器 应用代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
// 	
 

//音乐播放控制器
__audiodev audiodev;	  
 

//开始音频播放
void audio_start(void)
{
	audiodev.status=3<<0;//开始播放+非暂停
//	SAI_Play_Start();
} 

//关闭音频播放
void audio_stop(void)
{
	audiodev.status=0;
//	SAI_Play_Stop();
} 

//得到path路径下,目标文件的总个数
//path:路径		    
//返回值:总有效文件数
uint16_t audio_get_tnum(uint8_t *path)
{	  
	uint8_t res;
	uint16_t rval=0;
 	DIR tdir;	 		//临时目录
	FILINFO* tfileinfo;	//临时文件信息	 	
	tfileinfo=(FILINFO*)mymalloc(SRAMIN,sizeof(FILINFO));//申请内存
  res=f_opendir(&tdir,(const TCHAR*)path); //打开目录 
	if(res==FR_OK&&tfileinfo)
	{
		while(1)//查询总的有效文件数
		{
			res=f_readdir(&tdir,tfileinfo);       			//读取目录下的一个文件
			if(res!=FR_OK||tfileinfo->fname[0]==0)break;	//错误了/到末尾了,退出	 		 
			res=f_typetell((uint8_t*)tfileinfo->fname);	
			if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件	
			{
				rval++;//有效文件数增加1
			}	    
		}  
	}  
	myfree(SRAMIN,tfileinfo);//释放内存
	return rval;
}

//显示曲目索引
//index:当前索引
//total:总文件数
void audio_index_show(uint16_t index,uint16_t total)
{
	//显示当前曲目的索引,及总曲目数
	printf("%d/%d", index, total);     		  	  
}
 
//显示播放时间,比特率 信息  
//totsec;音频文件总时间长度
//cursec:当前播放时间
//bitrate:比特率(位速)
void audio_msg_show(uint32_t totsec,uint32_t cursec,uint32_t bitrate)
{	
	static uint16_t playtime=0XFFFF;//播放时间标记	      
	if(playtime!=cursec)					//需要更新显示时间
	{
		playtime=cursec;
		//显示播放时间			 
//		LCD_ShowxNum(60,210,playtime/60,2,16,0X80);		//分钟
//		LCD_ShowChar(60+16,210,':',16,0);
//		LCD_ShowxNum(60+24,210,playtime%60,2,16,0X80);	//秒钟		
// 		LCD_ShowChar(60+40,210,'/',16,0); 	    	 
//		//显示总时间    	   
// 		LCD_ShowxNum(60+48,210,totsec/60,2,16,0X80);	//分钟
//		LCD_ShowChar(60+64,210,':',16,0);
//		LCD_ShowxNum(60+72,210,totsec%60,2,16,0X80);	//秒钟	  		    
//		//显示位率			   
//   		LCD_ShowxNum(60+110,210,bitrate/1000,4,16,0X80);//显示位率	 
//		LCD_ShowString(60+110+32,210,200,16,16,"Kbps");	 
	} 		 
}
//播放音乐
void audio_play(void)
{
	uint8_t res;
 	DIR wavdir;	 		//目录
	FILINFO *wavfileinfo;//文件信息 
	uint8_t *pname;			//带路径的文件名
	uint16_t totwavnum; 		//音乐文件总数
	uint16_t curindex;		//当前索引
	uint8_t key;				//键值		  
 	uint32_t temp;
	uint32_t *wavoffsettbl;	//音乐offset索引表
    
	WM8978_Init();				    //初始化WM8978
	WM8978_HPvol_Set(40,40);	    //耳机音量设置
	WM8978_SPKvol_Set(50);		    //喇叭音量设置
	WM8978_ADDA_Cfg(1,0);	//开启DAC
	WM8978_Input_Cfg(0,0,0);//关闭输入通道
	WM8978_Output_Cfg(1,0);	//开启DAC输出   
	
 	while(f_opendir(&wavdir,"0:/MUSIC"))//打开音乐文件夹
 	{	    
		printf("MUSIC file folder error");     
		delay_ms(200);				  
	} 									  
	totwavnum=audio_get_tnum("0:/MUSIC"); //得到总有效文件数 
  while(totwavnum==NULL)//音乐文件总数为0		
 	{	    
		printf("can't find music file");
		delay_ms(200);				  		  
	}										   
	wavfileinfo=(FILINFO*)mymalloc(SRAMIN,sizeof(FILINFO));	//申请内存
  pname=mymalloc(SRAMIN,FF_MAX_LFN*2+1);					//为带路径的文件名分配内存
 	wavoffsettbl=mymalloc(SRAMIN,4*totwavnum);				//申请4*totwavnum个字节的内存,用于存放音乐文件off block索引
 	while(!wavfileinfo||!pname||!wavoffsettbl)//内存分配出错
 	{	    
		printf("memory allocation failed");
		delay_ms(200);				  			  
	} 	 
 	//记录索引
//  res=f_opendir(&wavdir,"0:/MUSIC"); //打开目录
//	if(res==FR_OK)
	{
		curindex=0;//当前索引为0
		while(1)//全部查询一遍
		{	
			temp=wavdir.dptr;								//记录当前index 
			res=f_readdir(&wavdir,wavfileinfo);       		//读取目录下的一个文件
			if(res!=FR_OK||wavfileinfo->fname[0]==0)break;	//错误了/到末尾了,退出 		 
			res=f_typetell((uint8_t*)wavfileinfo->fname);	
			if((res&0XF0)==0X40)//取高四位,看看是不是音乐文件	
			{
				wavoffsettbl[curindex]=temp;//记录索引
				curindex++;
			}	    
		} 
	}   
	curindex=0;											//从0开始显示
	res=f_opendir(&wavdir,(const TCHAR*)"0:/MUSIC"); 	//打开目录
	while(res==FR_OK)//打开成功
	{	
		dir_sdi(&wavdir,wavoffsettbl[curindex]);				//改变当前目录索引	   
		res=f_readdir(&wavdir,wavfileinfo);       				//读取目录下的一个文件
		if(res!=FR_OK||wavfileinfo->fname[0]==0)break;			//错误了/到末尾了,退出		 
		strcpy((char*)pname,"0:/MUSIC/");						//复制路径(目录)
		strcat((char*)pname,(const char*)wavfileinfo->fname);	//将文件名接在后面
//		Show_Str(60,190,lcddev.width-60,16,(uint8_t*)wavfileinfo->fname,16,0);//显示歌曲名字 
		audio_index_show(curindex+1,totwavnum);
		key=audio_play_song(pname); 			 		//播放这个音频文件
		if(key==KEY2_PRES)		//上一曲
		{
			if(curindex)curindex--;
			else curindex=totwavnum-1;
 		}else if(key==KEY0_PRES)//下一曲
		{
			curindex++;		   	
			if(curindex>=totwavnum)curindex=0;//到末尾的时候,自动从头开始
 		}else break;	//产生了错误 	 
	} 											  
	myfree(SRAMIN,wavfileinfo);			//释放内存			    
	myfree(SRAMIN,pname);				//释放内存			    
	myfree(SRAMIN,wavoffsettbl);		//释放内存	 
} 
//播放某个音频文件
uint8_t audio_play_song(uint8_t* fname)
{
	uint8_t res;  
	res=f_typetell(fname); 
	switch(res)
	{
		case T_WAV:
			res=wav_play_song(fname);
		break;
		
		case T_MP3:
			res=mp3_play_song(fname);	//播放MP3文件
		break;

		case T_FLAC:
			res=flac_play_song(fname);	//播放flac文件
		break;		
		
		default://其他文件,自动跳转到下一曲
			printf("can't play:%s\r\n",fname);
			res=KEY0_PRES;
		break;
	}
	return res;
}


#ifndef __BSP_AUDIOPLAY_H
#define __BSP_AUDIOPLAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "ff.h"
//#include "wavplay.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//音乐播放器 应用代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
// 	
 

//音乐播放控制器
typedef __packed struct
{  
	//2个SAI解码的BUF
	uint8_t *saibuf1;
	uint8_t *saibuf2; 
	uint8_t *tbuf;			//零时数组
	FIL *file;			//音频文件指针 	
	uint32_t(*file_seek)(uint32_t);//文件快进快退函数 

	volatile uint8_t status;			//bit0:0,暂停播放;1,继续播放
									//bit1:0,结束播放;1,开启播放  
									//bit2~3:保留
									//bit4:0,无音乐播放;1,音乐播放中 (对外标记)		
									//bit5:0,无动作;1,执行了一次切歌操作(对外标记)
									//bit6:0,无动作;1,请求终止播放(但是不删除音频播放任务),处理完成后,播放任务自动清零该位
									//bit7:0,音频播放任务已删除/请求删除;1,音频播放任务正在运行(允许继续执行)

	uint8_t mode;			//播放模式
	//0,全部循环;1,单曲循环;2,随机播放;

	uint8_t *path;			//当前文件夹路径
	uint8_t *name;			//当前播放的MP3歌曲名字
	uint16_t namelen;		//name所占的点数.
	uint16_t curnamepos;		//当前的偏移

	uint32_t totsec ;		//整首歌时长,单位:秒
	uint32_t cursec ;		//当前播放时长 
	uint32_t bitrate;	   	//比特率(位速)
	uint32_t samplerate;		//采样率 
	uint16_t bps;			//位数,比如16bit,24bit,32bit

	uint16_t curindex;		//当前播放的音频文件索引
	uint16_t mfilenum;		//音乐文件数目	    
	uint32_t *mfindextbl;	//音频文件索引表
}__audiodev; 
extern __audiodev audiodev;	//音乐播放控制器


void wav_sai_dma_callback(void);

void audio_start(void);
void audio_stop(void);
uint16_t audio_get_tnum(uint8_t *path);
void audio_index_show(uint16_t index,uint16_t total);
void audio_msg_show(uint32_t totsec,uint32_t cursec,uint32_t bitrate);
void audio_play(void);
uint8_t audio_play_song(uint8_t* fname);

//取2个值里面的较小值.
#ifndef AUDIO_MIN			
#define AUDIO_MIN(x,y)	((x)<(y)? (x):(y))
#endif

 
#ifdef __cplusplus
}
#endif

#endif /* __BSP_AUDIOPLAY_H */


#include "bsp_sai.h"
#include "bsp_wavplay.h"

/**
  * @brief SAI1 Initialization Function
  * @param None
  * @retval None
  */
void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency)
{
	HAL_SAI_DeInit(&hsai_BlockA1);
	
  hsai_BlockA1.Instance = SAI1_Block_A;
  hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
  hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
  hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
  hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
  hsai_BlockA1.Init.AudioFrequency = AudioFrequency;
  hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
  hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
  hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
  if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, datasize, 2) != HAL_OK)
  {
    Error_Handler();
  }
}

//SAI Block A采样率设置
//采样率计算公式:
//MCKDIV!=0: Fs=SAI_CK_x/[512*MCKDIV]
//MCKDIV==0: Fs=SAI_CK_x/256
//SAI_CK_x=(HSE/pllm)*PLLI2SN/PLLI2SQ/(PLLI2SDIVQ+1)
//一般HSE=25Mhz
//pllm:在Stm32_Clock_Init设置的时候确定,一般是25
//PLLI2SN:一般是192~432 
//PLLI2SQ:2~15 
//PLLI2SDIVQ:0~31
//MCKDIV:0~15 
//SAI A分频系数表@pllm=8,HSE=25Mhz,即vco输入频率为1Mhz 
const uint16_t SAI_PSC_TBL[][5]=
{
	{800 ,344,7,0,12},	//8Khz采样率
	{1102,429,2,18,2},	//11.025Khz采样率 
	{1600,344,7, 0,6},	//16Khz采样率
	{2205,429,2,18,1},	//22.05Khz采样率
	{3200,344,7, 0,3},	//32Khz采样率
	{4410,429,2,18,0},	//44.1Khz采样率
	{4800,344,7, 0,2},	//48Khz采样率
	{8820,271,2, 2,1},	//88.2Khz采样率
	{9600,344,7, 0,1},	//96Khz采样率
	{17640,271,6,0,0},	//176.4Khz采样率 
	{19200,295,6,0,0},	//192Khz采样率
};  
//设置SAIA的采样率(@MCKEN)
//samplerate:采样率,单位:Hz
//返回值:0,设置成功;1,无法设置.
uint8_t SAIA_SampleRate_Set(uint32_t samplerate)
{ 
	uint8_t i=0; 
	uint32_t tempreg=0;
	samplerate/=10;//缩小10倍   
	for(i=0;i<(sizeof(SAI_PSC_TBL)/10);i++)//看看改采样率是否可以支持
	{
		if(samplerate==SAI_PSC_TBL[i][0])break;
	}
	RCC->CR&=~(1<<26);						//先关闭PLLI2S  
	if(i==(sizeof(SAI_PSC_TBL)/10))return 1;//搜遍了也找不到
	tempreg|=(uint32_t)SAI_PSC_TBL[i][1]<<6;		//设置PLLI2SN
	tempreg|=(uint32_t)SAI_PSC_TBL[i][2]<<24;	//设置PLLI2SQ 
	RCC->PLLI2SCFGR=tempreg;				//设置I2SxCLK的频率 
	tempreg=RCC->DCKCFGR1;			
	tempreg&=~(0X1F);						//清空PLLI2SDIVQ设置.
	tempreg&=~(0X03<<20);					//清空SAI1ASRC设置.
	tempreg|=SAI_PSC_TBL[i][3]<<0;			//设置PLLI2SDIVQ 
	tempreg|=1<<20;							//设置SAI1A时钟来源为PLLI2SQ
	RCC->DCKCFGR1=tempreg;					//设置DCKCFGR寄存器 
	RCC->CR|=1<<26;							//开启I2S时钟
	while((RCC->CR&1<<27)==0);				//等待I2S时钟开启成功. 
	tempreg=SAI1_Block_A->CR1;			
	tempreg&=~(0X0F<<20);					//清除MCKDIV设置
	tempreg|=(uint32_t)SAI_PSC_TBL[i][4]<<20;	//设置MCKDIV
	tempreg|=1<<16;							//使能SAI1 Block A
	tempreg|=1<<17;							//使能DMA
	SAI1_Block_A->CR1=tempreg;				//配置MCKDIV,同时使能SAI1 Block A 
	return 0;
}  

void SAIA_TX_DMA_Init(uint8_t width)
{  
	/* Peripheral DMA init*/
	
	//HAL_DMA_DeInit(&hdma_sai1_a);
		
	if(width==16)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
	}
	else if(width==24)
	{
		hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
		hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	}

	hdma_sai1_a.Instance = DMA2_Stream1;
	hdma_sai1_a.Init.Channel = DMA_CHANNEL_0;
	hdma_sai1_a.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sai1_a.Init.Mode = DMA_NORMAL;
	hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
	hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
	hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
	hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
	hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
	if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
	{
		Error_Handler();
	}

	/* Several peripheral DMA handle pointers point to the same DMA handle.
	 Be aware that there is only one stream to perform all the requested DMAs. */
	__HAL_LINKDMA(&hsai_BlockA1,hdmarx,hdma_sai1_a);

	__HAL_LINKDMA(&hsai_BlockA1,hdmatx,hdma_sai1_a);
}    

static uint32_t SAI1_client =0;

void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{

  GPIO_InitTypeDef GPIO_InitStruct;
/* SAI1 */
    if(hsai->Instance==SAI1_Block_A)
    {
    /* Peripheral clock enable */
    if (SAI1_client == 0)
    {
       __HAL_RCC_SAI1_CLK_ENABLE();
    }
    SAI1_client ++;

    /**SAI1_A_Block_A GPIO Configuration
    PE2     ------> SAI1_MCLK_A
    PE4     ------> SAI1_FS_A
    PE5     ------> SAI1_SCK_A
    PE6     ------> SAI1_SD_A
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

     

    }
}

void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
{
/* SAI1 */
    if(hsai->Instance==SAI1_Block_A)
    {
    SAI1_client --;
    if (SAI1_client == 0)
      {
      /* Peripheral clock disable */
       __HAL_RCC_SAI1_CLK_DISABLE();
      }

    /**SAI1_A_Block_A GPIO Configuration
    PE2     ------> SAI1_MCLK_A
    PE4     ------> SAI1_FS_A
    PE5     ------> SAI1_SCK_A
    PE6     ------> SAI1_SD_A
    */
    HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);

    /* SAI1 DMA Deinit */
    HAL_DMA_DeInit(hsai->hdmarx);
    HAL_DMA_DeInit(hsai->hdmatx);
    }
}


#ifndef __BSP_SAI_H
#define __BSP_SAI_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency);
uint8_t SAIA_SampleRate_Set(uint32_t samplerate);
void SAIA_TX_DMA_Init(uint8_t width);	
 
#ifdef __cplusplus
}
#endif

#endif /* __BSP_SAI_H */


#include "bsp_wavplay.h" 
#include "bsp_audioplay.h"
#include "bsp_printf.h"
#include "delay.h" 
#include "bsp_malloc.h"
#include "ff.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_key.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved				
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式. 
// 	
 
__wavctrl wavctrl;		//WAV控制结构体
volatile uint8_t wavtransferend=0;	//sai传输完成标志
volatile uint8_t wavwitchbuf=0;		//saibufx指示标志
 
//WAV解析初始化
//fname:文件路径+文件名
//wavx:wav 信息存放结构体指针
//返回值:0,成功;1,打开文件失败;2,非WAV文件;3,DATA区域未找到.
uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx)
{
	FIL*ftemp;
	uint8_t *buf; 
	uint32_t br=0;
	uint8_t res=0;
	
	ChunkRIFF *riff;
	ChunkFMT *fmt;
	ChunkFACT *fact;
	ChunkDATA *data;
	ftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	buf=mymalloc(SRAMIN,512);
	if(ftemp&&buf)	//内存申请成功
	{
		res=f_open(ftemp,(TCHAR*)fname,FA_READ);//打开文件
		if(res==FR_OK)
		{
			f_read(ftemp,buf,512,&br);	//读取512字节在数据
			riff=(ChunkRIFF *)buf;		//获取RIFF块
			if(riff->Format==0X45564157)//是WAV文件
			{
				fmt=(ChunkFMT *)(buf+12);	//获取FMT块 
				fact=(ChunkFACT *)(buf+12+8+fmt->ChunkSize);//读取FACT块
				if(fact->ChunkID==0X74636166||fact->ChunkID==0X5453494C)wavx->datastart=12+8+fmt->ChunkSize+8+fact->ChunkSize;//具有fact/LIST块的时候(未测试)
				else wavx->datastart=12+8+fmt->ChunkSize;  
				data=(ChunkDATA *)(buf+wavx->datastart);	//读取DATA块
				if(data->ChunkID==0X61746164)//解析成功!
				{
					wavx->audioformat=fmt->AudioFormat;		//音频格式
					wavx->nchannels=fmt->NumOfChannels;		//通道数
					wavx->samplerate=fmt->SampleRate;		//采样率
					wavx->bitrate=fmt->ByteRate*8;			//得到位速
					wavx->blockalign=fmt->BlockAlign;		//块对齐
					wavx->bps=fmt->BitsPerSample;			//位数,16/24/32位
					
					wavx->datasize=data->ChunkSize;			//数据块大小
					wavx->datastart=wavx->datastart+8;		//数据流开始的地方. 
					 
					printf("wavx->audioformat:%d\r\n",wavx->audioformat);
					printf("wavx->nchannels:%d\r\n",wavx->nchannels);
					printf("wavx->samplerate:%d\r\n",wavx->samplerate);
					printf("wavx->bitrate:%d\r\n",wavx->bitrate);
					printf("wavx->blockalign:%d\r\n",wavx->blockalign);
					printf("wavx->bps:%d\r\n",wavx->bps);
					printf("wavx->datasize:%d\r\n",wavx->datasize);
					printf("wavx->datastart:%d\r\n",wavx->datastart);  
				}else res=3;//data区域未找到.
			}else res=2;//非wav文件
			
		}else res=1;//打开文件错误
	}
	f_close(ftemp);
	myfree(SRAMIN,ftemp);//释放内存
	myfree(SRAMIN,buf); 
	return 0;
}

//填充buf
//buf:数据区
//size:填充数据量
//bits:位数(16/24)
//返回值:读到的数据个数
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits)
{
	uint16_t readlen=0;
	uint32_t bread;
	uint16_t i;
	uint32_t *p,*pbuf;
	if(bits==24)//24bit音频,需要处理一下
	{
		readlen=(size/4)*3;		//此次要读取的字节数
		f_read(audiodev.file,audiodev.tbuf,readlen,(UINT*)&bread);//读取数据 
		pbuf=(uint32_t*)buf;
		for(i=0;i<size/4;i++)
		{  
			p=(uint32_t*)(audiodev.tbuf+i*3);
			pbuf[i]=p[0];  
		} 
		bread=(bread*4)/3;		//填充后的大小.
	}else 
	{
		f_read(audiodev.file,buf,size,(UINT*)&bread);//16bit音频,直接读取数据  
		if(bread<size)//不够数据了,补充0
		{
			for(i=bread;i<size-bread;i++)buf[i]=0; 
		}
	}
	return bread;
}  

//得到当前播放时间
//fx:文件指针
//wavx:wav播放控制器
void wav_get_curtime(FIL*fx,__wavctrl *wavx)
{
	long long fpos;  	
 	wavx->totsec=wavx->datasize/(wavx->bitrate/8);	//歌曲总长度(单位:秒) 
	fpos=fx->fptr-wavx->datastart; 					//得到当前文件播放到的地方 
	wavx->cursec=fpos*wavx->totsec/wavx->datasize;	//当前播放到第多少秒了?	
}

//播放某个WAV文件
//fname:wav文件路径.
//返回值:
//KEY0_PRES:下一曲
//KEY1_PRES:上一曲
//其他:错误
uint8_t wav_play_song(uint8_t* fname)
{
	uint8_t key;
	uint8_t t=0; 
	uint8_t res;  
	uint32_t fillnum; 
	audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	audiodev.saibuf1=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	audiodev.saibuf2=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	audiodev.tbuf=mymalloc(SRAMIN,WAV_SAI_TX_DMA_BUFSIZE);
	if(audiodev.file&&audiodev.saibuf1&&audiodev.saibuf2&&audiodev.tbuf)
	{ 
		res=wav_decode_init(fname,&wavctrl);//得到文件的信息
		if(res==0)//解析文件成功
		{
			if(wavctrl.bps==16)
			{
				WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度
				BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, wavctrl.samplerate);
				SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率   
				SAIA_TX_DMA_Init(16);	
			}
			else if(wavctrl.bps==24)
			{
				WM8978_I2S_Cfg(2,2);	//飞利浦标准,24位数据长度
				BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_24BIT, wavctrl.samplerate); 	
				SAIA_SampleRate_Set(wavctrl.samplerate);//设置采样率   	
				SAIA_TX_DMA_Init(24);
			}
			else
			{
				res=0XFF;
			}
			
			if(res==0)
			{
				res=f_open(audiodev.file,(TCHAR*)fname,FA_READ);	//打开文件
				if(res==0)
				{
					f_lseek(audiodev.file, wavctrl.datastart);		//跳过文件头
					audiodev.status=3<<0;//开始播放+非暂停
					wavwitchbuf = 0;
					wavtransferend=0;
					fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
					
					while(res==0)
					{
						if(wavwitchbuf == 0)
						{
							if(wavctrl.bps==16)
							{
								HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, WAV_SAI_TX_DMA_BUFSIZE/2);
							}
							else//24bit
							{
								HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, WAV_SAI_TX_DMA_BUFSIZE/4);
							}
							
							fillnum=wav_buffill(audiodev.saibuf2,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
							wavwitchbuf = 1;
						}
						else
						{
							if(wavctrl.bps==16)
							{
								HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/2);
							}
							else//24bit
							{
								HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, WAV_SAI_TX_DMA_BUFSIZE/4);
							}
							
							fillnum=wav_buffill(audiodev.saibuf1,WAV_SAI_TX_DMA_BUFSIZE,wavctrl.bps);
							wavwitchbuf = 0;
						}
						
						while(wavtransferend==0);//等待wav传输完成; 
						wavtransferend=0;
						if(fillnum!=WAV_SAI_TX_DMA_BUFSIZE)//播放结束?
						{
							res=KEY0_PRES;
							break;
						} 
						
						while(1)
						{
							key=KEY_Scan(0); 
							if(key==WKUP_PRES)//暂停
							{
								if(audiodev.status&0X01)audiodev.status&=~(1<<0);
								else audiodev.status|=0X01;  
							}
							if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲
							{
								res=key;
								break; 
							}
							wav_get_curtime(audiodev.file,&wavctrl);//得到总时间和当前播放的时间 
							audio_msg_show(wavctrl.totsec,wavctrl.cursec,wavctrl.bitrate);

							if((audiodev.status&0X01)==0)delay_ms(10);
							else break;
						}
					}
					audiodev.status=0;
				}
				else 
				{
					res=0XFF;
				}
			}
		}
		else
		{
			res=0XFF;
		} 
	}
	else
	{
		res=0XFF; 
	}		
	myfree(SRAMIN,audiodev.tbuf);	//释放内存
	myfree(SRAMIN,audiodev.saibuf1);//释放内存
	myfree(SRAMIN,audiodev.saibuf2);//释放内存 
	myfree(SRAMIN,audiodev.file);	//释放内存 
	return res;
} 


#ifndef __BSP_WAVPLAY_H
#define __BSP_WAVPLAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//WAV 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/18
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved				
//********************************************************************************
//V1.0 说明
//1,支持16位/24位WAV文件播放
//2,最高可以支持到192K/24bit的WAV格式. 
// 	
 

#define WAV_SAI_TX_DMA_BUFSIZE    4096		//定义WAV TX DMA 数组大小(播放192Kbps@24bit的时候,需要设置4096大才不会卡)
 
//RIFF块
typedef __packed struct
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"RIFF",即0X46464952
	uint32_t ChunkSize ;		   	//集合大小;文件总大小-8
	uint32_t Format;	   			//格式;WAVE,即0X45564157
}ChunkRIFF ;
//fmt块
typedef __packed struct
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"fmt ",即0X20746D66
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:20.
	uint16_t AudioFormat;	  	//音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
	uint16_t NumOfChannels;		//通道数量;1,表示单声道;2,表示双声道;
	uint32_t SampleRate;			//采样率;0X1F40,表示8Khz
	uint32_t ByteRate;			//字节速率; 
	uint16_t BlockAlign;			//块对齐(字节); 
	uint16_t BitsPerSample;		//单个采样数据大小;4位ADPCM,设置为4
//	uint16_t ByteExtraData;		//附加的数据字节;2个; 线性PCM,没有这个参数
}ChunkFMT;	   
//fact块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"fact",即0X74636166;
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:4.
	uint32_t NumOfSamples;	  	//采样的数量; 
}ChunkFACT;
//LIST块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"LIST",即0X74636166;
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size);这里为:4. 
}ChunkLIST;

//data块 
typedef __packed struct 
{
	uint32_t ChunkID;		   	//chunk id;这里固定为"data",即0X5453494C
	uint32_t ChunkSize ;		   	//子集合大小(不包括ID和Size) 
}ChunkDATA;

//wav头
typedef __packed struct
{ 
	ChunkRIFF riff;	//riff块
	ChunkFMT fmt;  	//fmt块
//	ChunkFACT fact;	//fact块 线性PCM,没有这个结构体	 
	ChunkDATA data;	//data块		 
}__WaveHeader; 

//wav 播放控制结构体
typedef __packed struct
{ 
	uint16_t audioformat;			//音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
	uint16_t nchannels;				//通道数量;1,表示单声道;2,表示双声道; 
	uint16_t blockalign;				//块对齐(字节);  
	uint32_t datasize;				//WAV数据大小 

	uint32_t totsec ;				//整首歌时长,单位:秒
	uint32_t cursec ;				//当前播放时长

	uint32_t bitrate;	   			//比特率(位速)
	uint32_t samplerate;				//采样率 
	uint16_t bps;					//位数,比如16bit,24bit,32bit

	uint32_t datastart;				//数据帧开始的位置(在文件里面的偏移)
}__wavctrl; 


uint8_t wav_decode_init(uint8_t* fname,__wavctrl* wavx);
uint32_t wav_buffill(uint8_t *buf,uint16_t size,uint8_t bits);
//void wav_sai_dma_tx_callback(void); 
uint8_t wav_play_song(uint8_t* fname);


volatile extern uint8_t wavtransferend;	//sai传输完成标志
extern __wavctrl wavctrl;		//WAV控制结构体

#ifdef __cplusplus
}
#endif

#endif /* __BSP_WAVPLAY_H */



#include "bsp_mp3play.h"
#include "bsp_audioplay.h"
//#include "sys.h"
#include "delay.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
//#include "ff.h"
#include "string.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_key.h" 
//#include "led.h" 
//	 
//本程序移植自helix MP3解码库
//ALIENTEK STM32开发板
//MP3 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16位单声道/立体声MP3的解码
//2,支持CBR/VBR格式MP3解码
//3,支持ID3V1和ID3V2标签解析
//4,支持所有比特率(MP3最高是320Kbps)解码
// 	
 
__mp3ctrl * mp3ctrl;	//mp3控制结构体 
volatile uint8_t mp3transferend=0;	//sai传输完成标志
volatile uint8_t mp3witchbuf=0;		//saibufx指示标志

//MP3 DMA发送回调函数
//void mp3_sai_dma_tx_callback(void) 
//{    
//	uint16_t i;
//	if(DMA2_Stream3->CR&(1<<19))
//	{
//		mp3witchbuf=0;
//		if((audiodev.status&0X01)==0)//暂停了,填充0
//		{
//			for(i=0;i<2304*2;i++)audiodev.saibuf1[i]=0;
//		}
//	}else 
//	{
//		mp3witchbuf=1;
//		if((audiodev.status&0X01)==0)//暂停了,填充0
//		{
//			for(i=0;i<2304*2;i++)audiodev.saibuf2[i]=0;
//		}
//	} 
//	mp3transferend=1;
//} 

//填充PCM数据到DAC
//buf:PCM数据首地址
//size:pcm数据量(16位为单位)
//nch:声道数(1,单声道,2立体声)
void mp3_fill_buffer(uint16_t* buf,uint16_t size,uint8_t nch)
{
	uint16_t i; 
	uint16_t *p;
	
	if(mp3witchbuf==0)
	{
		p=(uint16_t*)audiodev.saibuf1;
	}else 
	{
		p=(uint16_t*)audiodev.saibuf2;
	}
	if(nch==2)for(i=0;i<size;i++)p[i]=buf[i];
	else	//单声道
	{
		for(i=0;i<size;i++)
		{
			p[2*i]=buf[i];
			p[2*i+1]=buf[i];
		}
	}
	
	while(mp3transferend==0);//等待传输完成
	mp3transferend=0;
	
	if(mp3witchbuf == 0)
	{
		HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, mp3ctrl->outsamples);
		mp3witchbuf = 1;
	}
	else
	{
		HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, mp3ctrl->outsamples);
		mp3witchbuf = 0;
	}
} 

//解析ID3V1 
//buf:输入数据缓存区(大小固定是128字节)
//pctrl:MP3控制器
//返回值:0,获取正常
//    其他,获取失败
uint8_t mp3_id3v1_decode(uint8_t* buf,__mp3ctrl *pctrl)
{
	ID3V1_Tag *id3v1tag;
	id3v1tag=(ID3V1_Tag*)buf;
	if (strncmp("TAG",(char*)id3v1tag->id,3)==0)//是MP3 ID3V1 TAG
	{
		if(id3v1tag->title[0])strncpy((char*)pctrl->title,(char*)id3v1tag->title,30);
		if(id3v1tag->artist[0])strncpy((char*)pctrl->artist,(char*)id3v1tag->artist,30); 
	}else return 1;
	return 0;
}
//解析ID3V2 
//buf:输入数据缓存区
//size:数据大小
//pctrl:MP3控制器
//返回值:0,获取正常
//    其他,获取失败
uint8_t mp3_id3v2_decode(uint8_t* buf,uint32_t size,__mp3ctrl *pctrl)
{
	ID3V2_TagHead *taghead;
	ID3V23_FrameHead *framehead; 
	uint32_t t;
	uint32_t tagsize;	//tag大小
	uint32_t frame_size;	//帧大小 
	taghead=(ID3V2_TagHead*)buf; 
	if(strncmp("ID3",(const char*)taghead->id,3)==0)//存在ID3?
	{
		tagsize=((uint32_t)taghead->size[0]<<21)|((uint32_t)taghead->size[1]<<14)|((uint16_t)taghead->size[2]<<7)|taghead->size[3];//得到tag 大小
		pctrl->datastart=tagsize;		//得到mp3数据开始的偏移量
		if(tagsize>size)tagsize=size;	//tagsize大于输入bufsize的时候,只处理输入size大小的数据
		if(taghead->mversion<3)
		{
			printf("not supported mversion!\r\n");
			return 1;
		}
		t=10;
		while(t<tagsize)
		{
			framehead=(ID3V23_FrameHead*)(buf+t);
			frame_size=((uint32_t)framehead->size[0]<<24)|((uint32_t)framehead->size[1]<<16)|((uint32_t)framehead->size[2]<<8)|framehead->size[3];//得到帧大小
 			if (strncmp("TT2",(char*)framehead->id,3)==0||strncmp("TIT2",(char*)framehead->id,4)==0)//找到歌曲标题帧,不支持unicode格式!!
			{
				strncpy((char*)pctrl->title,(char*)(buf+t+sizeof(ID3V23_FrameHead)+1),AUDIO_MIN(frame_size-1,MP3_TITSIZE_MAX-1));
			}
 			if (strncmp("TP1",(char*)framehead->id,3)==0||strncmp("TPE1",(char*)framehead->id,4)==0)//找到歌曲艺术家帧
			{
				strncpy((char*)pctrl->artist,(char*)(buf+t+sizeof(ID3V23_FrameHead)+1),AUDIO_MIN(frame_size-1,MP3_ARTSIZE_MAX-1));
			}
			t+=frame_size+sizeof(ID3V23_FrameHead);
		} 
	}else pctrl->datastart=0;//不存在ID3,mp3数据是从0开始
	return 0;
} 

//获取MP3基本信息
//pname:MP3文件路径
//pctrl:MP3控制信息结构体 
//返回值:0,成功
//    其他,失败
uint8_t mp3_get_info(uint8_t *pname,__mp3ctrl* pctrl)
{
    HMP3Decoder decoder;
    MP3FrameInfo frame_info;
	MP3_FrameXing* fxing;
	MP3_FrameVBRI* fvbri;
	FIL*fmp3;
	uint8_t *buf;
	uint32_t br;
	uint8_t res;
	int offset=0;
	uint32_t p;
	short samples_per_frame;	//一帧的采样个数
	uint32_t totframes;				//总帧数
	
	fmp3=mymalloc(SRAMIN,sizeof(FIL)); 
	buf=mymalloc(SRAMIN,5*1024);		//申请5K内存 
	if(fmp3&&buf)//内存申请成功
	{ 		
		f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开文件
		res=f_read(fmp3,(char*)buf,5*1024,&br);
		if(res==0)//读取文件成功,开始解析ID3V2/ID3V1以及获取MP3信息
		{  
			mp3_id3v2_decode(buf,br,pctrl);	//解析ID3V2数据
			f_lseek(fmp3,fmp3->obj.objsize-128);	//偏移到倒数128的位置
			f_read(fmp3,(char*)buf,128,&br);//读取128字节
			mp3_id3v1_decode(buf,pctrl);	//解析ID3V1数据  
			decoder=MP3InitDecoder(); 		//MP3解码申请内存
			f_lseek(fmp3,pctrl->datastart);	//偏移到数据开始的地方
			f_read(fmp3,(char*)buf,5*1024,&br);	//读取5K字节mp3数据
 			offset=MP3FindSyncWord(buf,br);	//查找帧同步信息
			if(offset>=0&&MP3GetNextFrameInfo(decoder,&frame_info,&buf[offset])==0)//找到帧同步信息了,且下一阵信息获取正常	
			{ 
				p=offset+4+32;
				fvbri=(MP3_FrameVBRI*)(buf+p);
				if(strncmp("VBRI",(char*)fvbri->id,4)==0)//存在VBRI帧(VBR格式)
				{
					if (frame_info.version==MPEG1)samples_per_frame=1152;//MPEG1,layer3每帧采样数等于1152
					else samples_per_frame=576;//MPEG2/MPEG2.5,layer3每帧采样数等于576 
 					totframes=((uint32_t)fvbri->frames[0]<<24)|((uint32_t)fvbri->frames[1]<<16)|((uint16_t)fvbri->frames[2]<<8)|fvbri->frames[3];//得到总帧数
					pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度
				}else	//不是VBRI帧,尝试是不是Xing帧(VBR格式)
				{  
					if (frame_info.version==MPEG1)	//MPEG1 
					{
						p=frame_info.nChans==2?32:17;
						samples_per_frame = 1152;	//MPEG1,layer3每帧采样数等于1152
					}else
					{
						p=frame_info.nChans==2?17:9;
						samples_per_frame=576;		//MPEG2/MPEG2.5,layer3每帧采样数等于576
					}
					p+=offset+4;
					fxing=(MP3_FrameXing*)(buf+p);
					if(strncmp("Xing",(char*)fxing->id,4)==0||strncmp("Info",(char*)fxing->id,4)==0)//是Xng帧
					{
						if(fxing->flags[3]&0X01)//存在总frame字段
						{
							totframes=((uint32_t)fxing->frames[0]<<24)|((uint32_t)fxing->frames[1]<<16)|((uint16_t)fxing->frames[2]<<8)|fxing->frames[3];//得到总帧数
							pctrl->totsec=totframes*samples_per_frame/frame_info.samprate;//得到文件总长度
						}else	//不存在总frames字段
						{
							pctrl->totsec=fmp3->obj.objsize/(frame_info.bitrate/8);
						} 
					}else 		//CBR格式,直接计算总播放时间
					{
						pctrl->totsec=fmp3->obj.objsize/(frame_info.bitrate/8);
					}
				} 
				pctrl->bitrate=frame_info.bitrate;			//得到当前帧的码率
				mp3ctrl->samplerate=frame_info.samprate; 	//得到采样率. 
				if(frame_info.nChans==2)mp3ctrl->outsamples=frame_info.outputSamps; //输出PCM数据量大小 
				else mp3ctrl->outsamples=frame_info.outputSamps*2; //输出PCM数据量大小,对于单声道MP3,直接*2,补齐为双声道输出
			}else res=0XFE;//未找到同步帧	
			MP3FreeDecoder(decoder);//释放内存		
		} 
		f_close(fmp3);
	}else res=0XFF;
	myfree(SRAMIN,fmp3);
	myfree(SRAMIN,buf);	
	return res;	
}  
//得到当前播放时间
//fx:文件指针
//mp3x:mp3播放控制器
void mp3_get_curtime(FIL*fx,__mp3ctrl *mp3x)
{
	uint32_t fpos=0;  	 
	if(fx->fptr>mp3x->datastart)fpos=fx->fptr-mp3x->datastart;	//得到当前文件播放到的地方 
	mp3x->cursec=fpos*mp3x->totsec/(fx->obj.objsize-mp3x->datastart);	//当前播放到第多少秒了?	
}
//mp3文件快进快退函数
//pos:需要定位到的文件位置
//返回值:当前文件位置(即定位后的结果)
uint32_t mp3_file_seek(uint32_t pos)
{
	if(pos>audiodev.file->obj.objsize)
	{
		pos=audiodev.file->obj.objsize;
	}
	f_lseek(audiodev.file,pos);
	return audiodev.file->fptr;
}
//播放一曲MP3音乐
//fname:MP3文件路径.
//返回值:0,正常播放完成
//[b7]:0,正常状态;1,错误状态
//[b6:0]:b7=0时,表示操作码 
//       b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误)
uint8_t mp3_play_song(uint8_t* fname)
{ 
	HMP3Decoder mp3decoder;
	MP3FrameInfo mp3frameinfo;
	uint8_t key;
	uint8_t res;
	uint8_t* buffer;		//输入buffer  
	uint8_t* readptr;	//MP3解码读指针
	int offset=0;	//偏移量
	int outofdata=0;//超出数据范围
	int bytesleft=0;//buffer还剩余的有效数据
	uint32_t br=0; 
	int err=0;  
	
	mp3transferend=0;	//sai传输完成标志
	mp3witchbuf=0;		//saibufx指示标志
	
 	mp3ctrl=mymalloc(SRAMIN,sizeof(__mp3ctrl)); 
	buffer=mymalloc(SRAMIN,MP3_FILE_BUF_SZ); 	//申请解码buf大小
	audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	audiodev.saibuf1=mymalloc(SRAMIN,2304*2);
	audiodev.saibuf2=mymalloc(SRAMIN,2304*2);
	audiodev.tbuf=mymalloc(SRAMIN,2304*2);
	audiodev.file_seek=mp3_file_seek;
	
	if(!mp3ctrl||!buffer||!audiodev.file||!audiodev.saibuf1||!audiodev.saibuf2||!audiodev.tbuf)//内存申请失败
	{
		myfree(SRAMIN,mp3ctrl);
		myfree(SRAMIN,buffer);
		myfree(SRAMIN,audiodev.file);
		myfree(SRAMIN,audiodev.saibuf1);
		myfree(SRAMIN,audiodev.saibuf2);
		myfree(SRAMIN,audiodev.tbuf); 
		return 0xff;	//错误
	} 
	memset(audiodev.saibuf1,0,2304*2);	//数据清零 
	memset(audiodev.saibuf2,0,2304*2);	//数据清零 
	memset(mp3ctrl,0,sizeof(__mp3ctrl));//数据清零 
	res=mp3_get_info(fname,mp3ctrl);  
	if(res==0)
	{ 
		printf("     title:%s\r\n",mp3ctrl->title); 
		printf("    artist:%s\r\n",mp3ctrl->artist); 
		printf("   bitrate:%dbps\r\n",mp3ctrl->bitrate);	
		printf("samplerate:%d\r\n", mp3ctrl->samplerate);	
		printf("  totalsec:%d\r\n",mp3ctrl->totsec); 
		
		WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度
		BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, mp3ctrl->samplerate);
		SAIA_SampleRate_Set(mp3ctrl->samplerate);//设置采样率   
		SAIA_TX_DMA_Init(16);	
		
		mp3decoder=MP3InitDecoder(); 					//MP3解码申请内存
		res=f_open(audiodev.file,(char*)fname,FA_READ);	//打开文件
	}
	if(res==0&&mp3decoder!=0)//打开文件成功
	{ 
		f_lseek(audiodev.file,mp3ctrl->datastart);	//跳过文件头中tag信息
		audio_start();								//开始播放 
		HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, mp3ctrl->outsamples);
		while(res==0)
		{
			readptr=buffer;	//MP3读指针指向buffer
			offset=0;		//偏移量为0
			outofdata=0;	//数据正常
			bytesleft=0;	
			res=f_read(audiodev.file,buffer,MP3_FILE_BUF_SZ,&br);//一次读取MP3_FILE_BUF_SZ字节
			if(res)//读数据出错了
			{
				res=0xff;
				break;
			}
			if(br==0)		//读数为0,说明解码完成了.
			{
				res=KEY0_PRES;	//播放完成
				break;
			}
			bytesleft+=br;	//buffer里面有多少有效MP3数据?
			err=0;			
			while(!outofdata)//没有出现数据异常(即可否找到帧同步字符)
			{
				offset=MP3FindSyncWord(readptr,bytesleft);//在readptr位置,开始查找同步字符
				if(offset<0)	//没有找到同步字符,跳出帧解码循环
				{ 
					outofdata=1;//没找到帧同步字符
				}else	//找到同步字符了
				{
					readptr+=offset;		//MP3读指针偏移到同步字符处.
					bytesleft-=offset;		//buffer里面的有效数据个数,必须减去偏移量
					err=MP3Decode(mp3decoder,&readptr,&bytesleft,(short*)audiodev.tbuf,0);//解码一帧MP3数据
					if(err!=0)
					{
						printf("decode error:%d\r\n",err);
						break;
					}else
					{
						MP3GetLastFrameInfo(mp3decoder,&mp3frameinfo);	//得到刚刚解码的MP3帧信息
						if(mp3ctrl->bitrate!=mp3frameinfo.bitrate)		//更新码率
						{
							mp3ctrl->bitrate=mp3frameinfo.bitrate; 
						}
						mp3_fill_buffer((uint16_t*)audiodev.tbuf,mp3frameinfo.outputSamps,mp3frameinfo.nChans);//填充pcm数据
					}
					if(bytesleft<MAINBUF_SIZE*2)//当数组内容小于2倍MAINBUF_SIZE的时候,必须补充新的数据进来.
					{ 
						memmove(buffer,readptr,bytesleft);//移动readptr所指向的数据到buffer里面,数据量大小为:bytesleft
						f_read(audiodev.file,buffer+bytesleft,MP3_FILE_BUF_SZ-bytesleft,&br);//补充余下的数据
						if(br<MP3_FILE_BUF_SZ-bytesleft)
						{
							memset(buffer+bytesleft+br,0,MP3_FILE_BUF_SZ-bytesleft-br); 
						}
						bytesleft=MP3_FILE_BUF_SZ;  
						readptr=buffer; 
					} 	
// 					while(audiodev.status&(1<<1))//正常播放中
//					{			 
//						delay_ms(1000/OS_TICKS_PER_SEC);
//						mp3_get_curtime(audiodev.file,mp3ctrl); 
//						audiodev.totsec=mp3ctrl->totsec;	//参数传递
//						audiodev.cursec=mp3ctrl->cursec;
//						audiodev.bitrate=mp3ctrl->bitrate;
//						audiodev.samplerate=mp3ctrl->samplerate;
//						audiodev.bps=16;//MP3仅支持16位
// 						if(audiodev.status&0X01)break;//没有按下暂停 
//					}
//					if((audiodev.status&(1<<1))==0)//请求结束播放/播放完成
//					{  
//						res=AP_NEXT;//跳出上上级循环
//						outofdata=1;//跳出上一级循环
//						break;
//					}  
					
					while(1)
					{
						key=KEY_Scan(0); 
						if(key==WKUP_PRES)//暂停
						{
							if(audiodev.status&0X01)audiodev.status&=~(1<<0);
							else audiodev.status|=0X01;  
						}
						if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲
						{
							outofdata=1;
							res=key;
							break; 
						}
						mp3_get_curtime(audiodev.file,mp3ctrl); 
						audiodev.totsec=mp3ctrl->totsec;	//参数传递
						audiodev.cursec=mp3ctrl->cursec;
						audiodev.bitrate=mp3ctrl->bitrate;
						audiodev.samplerate=mp3ctrl->samplerate;
						audiodev.bps=16;//MP3仅支持16位

						if((audiodev.status&0X01)==0)
						{
							delay_ms(10);
						}	
						else//正常播放
						{
							break;
						}							
					}
					
				}					
			}  
		}
		audio_stop();//关闭音频输出
	}else res=0xff;//错误
	f_close(audiodev.file);
	MP3FreeDecoder(mp3decoder);		//释放内存	
	myfree(SRAMIN,mp3ctrl);
	myfree(SRAMIN,buffer);
	myfree(SRAMIN,audiodev.file);
	myfree(SRAMIN,audiodev.saibuf1);
	myfree(SRAMIN,audiodev.saibuf2);
	myfree(SRAMIN,audiodev.tbuf);
	return res;
}




#ifndef __BSP_MP3PLAY_H
#define __BSP_MP3PLAY_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
	
#include <mp3dec.h>

//	 
//本程序移植自helix MP3解码库
//ALIENTEK STM32开发板
//MP3 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16位单声道/立体声MP3的解码
//2,支持CBR/VBR格式MP3解码
//3,支持ID3V1和ID3V2标签解析
//4,支持所有比特率(MP3最高是320Kbps)解码
// 	
  
#define MP3_TITSIZE_MAX		40		//歌曲名字最大长度
#define MP3_ARTSIZE_MAX		40		//歌曲名字最大长度
#define MP3_FILE_BUF_SZ    5*1024	//MP3解码时,文件buf大小
 
//ID3V1 标签 
typedef __packed struct 
{
    uint8_t id[3];		   	//ID,TAG三个字母
    uint8_t title[30];		//歌曲名字
    uint8_t artist[30];		//艺术家名字
	uint8_t year[4];			//年代
	uint8_t comment[30];		//备注
	uint8_t genre;			//流派 
}ID3V1_Tag;

//ID3V2 标签头 
typedef __packed struct 
{
    uint8_t id[3];		   	//ID
    uint8_t mversion;		//主版本号
    uint8_t sversion;		//子版本号
    uint8_t flags;			//标签头标志
    uint8_t size[4];			//标签信息大小(不包含标签头10字节).所以,标签大小=size+10.
}ID3V2_TagHead;

//ID3V2.3 版本帧头
typedef __packed struct 
{
    uint8_t id[4];		   	//帧ID
    uint8_t size[4];			//帧大小
    uint16_t flags;			//帧标志
}ID3V23_FrameHead;

//MP3 Xing帧信息(没有全部列出来,仅列出有用的部分)
typedef __packed struct 
{
    uint8_t id[4];		   	//帧ID,为Xing/Info
    uint8_t flags[4];		//存放标志
    uint8_t frames[4];		//总帧数
	uint8_t fsize[4];		//文件总大小(不包含ID3)
}MP3_FrameXing;
 
//MP3 VBRI帧信息(没有全部列出来,仅列出有用的部分)
typedef __packed struct 
{
    uint8_t id[4];		   	//帧ID,为Xing/Info
	uint8_t version[2];		//版本号
	uint8_t delay[2];		//延迟
	uint8_t quality[2];		//音频质量,0~100,越大质量越好
	uint8_t fsize[4];		//文件总大小
	uint8_t frames[4];		//文件总帧数 
}MP3_FrameVBRI;


//MP3控制结构体
typedef __packed struct 
{
    uint8_t title[MP3_TITSIZE_MAX];	//歌曲名字
    uint8_t artist[MP3_ARTSIZE_MAX];	//艺术家名字
    uint32_t totsec ;				//整首歌时长,单位:秒
    uint32_t cursec ;				//当前播放时长
	
    uint32_t bitrate;	   			//比特率
	uint32_t samplerate;				//采样率
	uint16_t outsamples;				//PCM输出数据量大小(以16位为单位),单声道MP3,则等于实际输出*2(方便DAC输出)
	
	uint32_t datastart;				//数据帧开始的位置(在文件里面的偏移)
}__mp3ctrl;

extern __mp3ctrl * mp3ctrl;



void mp3_i2s_dma_tx_callback(void) ;
void mp3_fill_buffer(uint16_t* buf,uint16_t size,uint8_t nch);
uint8_t mp3_id3v1_decode(uint8_t* buf,__mp3ctrl *pctrl);
uint8_t mp3_id3v2_decode(uint8_t* buf,uint32_t size,__mp3ctrl *pctrl);
uint8_t mp3_get_info(uint8_t *pname,__mp3ctrl* pctrl);
uint8_t mp3_play_song(uint8_t* fname);
	
extern volatile uint8_t mp3transferend;
	
	
#ifdef __cplusplus
}
#endif

#endif /* __BSP_MP3PLAY_H */

#include "bsp_flacplay.h"
#include "ff.h"
#include "bsp_sai.h"
#include "bsp_wm8978.h"
#include "bsp_malloc.h"
#include "bsp_printf.h"
#include "bsp_key.h"
//#include "led.h"
#include "delay.h"
#include "bsp_audioplay.h"

//	 
//本程序移植自RockBox的flac解码库
//ALIENTEK STM32开发板
//FLAC 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16/24位单声道/立体声flac的解码
//2,最高支持192K/16bit或96K/24bit的flac解码  
// 	 
 
__flacctrl * flacctrl;	//flac解码控制结构体

 
//分析FLAC文件
//fx:flac文件指针
//fc:flac解码容器
//返回值:0,分析成功
//    其他,错误代码
uint8_t flac_init(FIL* fx,__flacctrl* fctrl,FLACContext* fc)
{
	FLAC_Tag * flactag;
	MD_Block_Head *flacblkh;
	uint8_t *buf; 
	uint8_t endofmetadata=0;			//最后一个metadata标记
	int blocklength; 
	uint32_t br;
	uint8_t res;

	buf=mymalloc(SRAMIN,512);	//申请512字节内存
	if(!buf)return 1;			//内存申请失败 
	f_lseek(fx,0);				//偏移到文件头
	f_read(fx,buf,4,&br);		//读取4字节 
	flactag=(FLAC_Tag*)buf;		//强制转换为flac tag标签
	if(strncmp("fLaC",(char*)flactag->id,4)!=0) 
	{
		myfree(SRAMIN,buf);		//释放内存
		return 2;				//非flac文件
    } 
    while(!endofmetadata) 
	{
		f_read(fx,buf,4,&br);
        if(br<4)break;
		flacblkh=(MD_Block_Head*)buf;
		endofmetadata=flacblkh->head&0X80;	//判断是不是最后一个block?
		blocklength=((uint32_t)flacblkh->size[0]<<16)|((uint16_t)flacblkh->size[1]<<8)|(flacblkh->size[2]);//得到块大小
        if((flacblkh->head&0x7f)==0) 		//head最低7位为0,则表示是STREAMINFO块
        { 
			res=f_read(fx,buf,blocklength,&br);
            if(res!=FR_OK)break;  
            fc->min_blocksize=((uint16_t)buf[0]<<8) |buf[1];					//最小块大小
            fc->max_blocksize=((uint16_t)buf[2]<<8) |buf[3];					//最大块大小
            fc->min_framesize=((uint32_t)buf[4]<<16)|((uint16_t)buf[5]<<8)|buf[6];//最小帧大小
            fc->max_framesize=((uint32_t)buf[7]<<16)|((uint16_t)buf[8]<<8)|buf[9];//最大帧大小
            fc->samplerate=((uint32_t)buf[10]<<12)|((uint16_t)buf[11]<<4)|((buf[12]&0xf0)>>4);//采样率
            fc->channels=((buf[12]&0x0e)>>1)+1;							//音频通道数
            fc->bps=((((uint16_t)buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1;	//采样位数16?24?32? 
            fc->totalsamples=((uint32_t)buf[14]<<24)|((uint32_t)buf[15]<<16)|((uint16_t)buf[16]<<8)|buf[17];//一个声道的总采样数
			fctrl->samplerate=fc->samplerate;
			fctrl->totsec=(fc->totalsamples/fc->samplerate);//得到总时间 
        }else 	//忽略其他帧的处理 
		{ 
            if(f_lseek(fx,fx->fptr+blocklength)!=FR_OK)
            { 
				myfree(SRAMIN,buf);
				return 3;
            }
		}
    } 
	myfree(SRAMIN,buf);//释放内存.
	if(fctrl->totsec)
	{
		fctrl->outsamples=fc->max_blocksize*2;//PCM输出数据量(*2,表示2个声道的数据量)
		fctrl->bps=fc->bps;			//采样位数(16/24/32)
		fctrl->datastart=fx->fptr;	//FLAC数据帧开始的地址
		fctrl->bitrate=((fx->obj.objsize-fctrl->datastart)*8)/fctrl->totsec;//得到FLAC的位速
	}else return 4;	//总时间为0?有问题的flac文件
	return 0;
} 
volatile uint8_t flactransferend=0;	//sai传输完成标志
volatile uint8_t flacwitchbuf=0;		//saibufx指示标志 

FLAC DMA发送回调函数
//void flac_sai_dma_tx_callback(void) 
//{    
//	uint16_t i;
//	uint16_t size;
//	if(DMA2_Stream3->CR&(1<<19))
//	{
//		flacwitchbuf=0;
//		if((audiodev.status&0X01)==0)//暂停了,填充0
//		{ 
//			if(flacctrl->bps==24)size=flacctrl->outsamples*4;
//			else size=flacctrl->outsamples*2;
//			for(i=0;i<size;i++)audiodev.saibuf1[i]=0;
//		}
//	}else 
//	{
//		flacwitchbuf=1;
//		if((audiodev.status&0X01)==0)//暂停了,填充0
//		{
//			if(flacctrl->bps==24)size=flacctrl->outsamples*4;
//			else size=flacctrl->outsamples*2;
//			for(i=0;i<size;i++)audiodev.saibuf2[i]=0;
//		}
//	} 
//	flactransferend=1;
//} 

//得到当前播放时间
//fx:文件指针
//flacctrl:flac播放控制器
void flac_get_curtime(FIL*fx,__flacctrl *flacctrl)
{
	long long fpos=0;  	 
	if(fx->fptr>flacctrl->datastart)fpos=fx->fptr-flacctrl->datastart;	//得到当前文件播放到的地方 
	flacctrl->cursec=fpos*flacctrl->totsec/(fx->obj.objsize-flacctrl->datastart);	//当前播放到第多少秒了?	
}
//flac文件快进快退函数
//pos:需要定位到的文件位置
//返回值:当前文件位置(即定位后的结果)
uint32_t flac_file_seek(uint32_t pos)
{
	if(pos>audiodev.file->obj.objsize)
	{
		pos=audiodev.file->obj.objsize;
	}
	f_lseek(audiodev.file,pos);
	return audiodev.file->fptr;
}
//播放一曲FLAC音乐
//fname:FLAC文件路径.
//返回值:0,正常播放完成
//[b7]:0,正常状态;1,错误状态
//[b6:0]:b7=0时,表示操作码 
//       b7=1时,表示有错误(这里不判定具体错误,0X80~0XFF,都算是错误)
uint8_t flac_play_song(uint8_t* fname) 
{ 
	FLACContext *fc=0; 
	int bytesleft;
	int consumed;
	uint8_t res=0;  
	uint32_t br=0; 
	uint8_t* buffer=0;    
	uint8_t* decbuf0=0;   
	uint8_t* decbuf1=0;  
	uint8_t* p8=0;    
	uint32_t flac_fptr=0; 
	uint8_t key;
	
 	fc=mymalloc(SRAMIN,sizeof(FLACContext)); 
	flacctrl=mymalloc(SRAMIN,sizeof(__flacctrl)); 
	audiodev.file=(FIL*)mymalloc(SRAMIN,sizeof(FIL));
	audiodev.file_seek=flac_file_seek;
	if(!fc||!audiodev.file||!flacctrl)res=1;//内存申请错误
	else
	{ 
		memset(fc,0,sizeof(FLACContext));//fc所有内容清零 
		res=f_open(audiodev.file,(char*)fname,FA_READ); //读取文件错误 
		if(res==FR_OK)
		{
			res=flac_init(audiodev.file,flacctrl,fc);	//flac解码初始化   
			if(fc->min_blocksize==fc->max_blocksize&&fc->max_blocksize!=0)//必须min_blocksize等于max_blocksize
			{
				if(fc->bps==24)	//24位音频数据
				{	
					audiodev.saibuf1=mymalloc(SRAMIN,fc->max_blocksize*8);
					audiodev.saibuf2=mymalloc(SRAMIN,fc->max_blocksize*8);  
				}else			//16位音频数据
				{
					audiodev.saibuf1=mymalloc(SRAMIN,fc->max_blocksize*4);
					audiodev.saibuf2=mymalloc(SRAMIN,fc->max_blocksize*4); 
				}
				buffer=mymalloc(SRAMDTCM,fc->max_framesize); 	//申请解码帧缓存 
				decbuf0=mymalloc(SRAMDTCM,fc->max_blocksize*4);
				decbuf1=mymalloc(SRAMDTCM,fc->max_blocksize*4);
			}else res+=1;//不支持的音频格式  
		}
	}
	if(buffer&&audiodev.saibuf1&&audiodev.saibuf2&&decbuf0&&decbuf1&&res==0)
	{ 
		printf("\r\n  Blocksize: %d .. %d\r\n", fc->min_blocksize,fc->max_blocksize);
		printf("  Framesize: %d .. %d\r\n",fc->min_framesize,fc->max_framesize);
		printf("  Samplerate: %d\r\n", fc->samplerate);
		printf("  Channels: %d\r\n", fc->channels);
		printf("  Bits per sample: %d\r\n", fc->bps);
		printf("  Metadata length: %d\r\n", flacctrl->datastart);
		printf("  Total Samples: %lu\r\n",fc->totalsamples);
		printf("  Duration: %d s\r\n",flacctrl->totsec);
		printf("  Bitrate: %d kbps\r\n",flacctrl->bitrate); 
		if(flacctrl->bps==24)		//24位音频数据
		{
//			WM8978_I2S_Cfg(2,2);	//飞利浦标准,24位数据长度
//			SAIA_Init(0,1,6);		//设置SAI,主发送,24位数据 
//			SAIA_SampleRate_Set(fc->samplerate);	//设置采样率  
//			SAIA_TX_DMA_Init(audiodev.saibuf1,audiodev.saibuf2,flacctrl->outsamples,2);//配置TX DMA
			
			WM8978_I2S_Cfg(2,2);	//飞利浦标准,24位数据长度
			BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_24BIT, fc->samplerate);
			SAIA_SampleRate_Set(fc->samplerate);	//设置采样率  
			SAIA_TX_DMA_Init(24);
			
			memset(audiodev.saibuf1,0,fc->max_blocksize*8);
			memset(audiodev.saibuf2,0,fc->max_blocksize*8);
		}else						//16位音频数据
		{
//			WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度
//			SAIA_Init(0,1,4);		//设置SAI,主发送,16位数据
//			SAIA_SampleRate_Set(fc->samplerate);	//设置采样率    
//			SAIA_TX_DMA_Init(audiodev.saibuf1,audiodev.saibuf2,flacctrl->outsamples,1);//配置TX DMA		
			
			WM8978_I2S_Cfg(2,0);	//飞利浦标准,16位数据长度
			BSP_SAI1_Init(SAI_PROTOCOL_DATASIZE_16BIT, fc->samplerate);
			SAIA_SampleRate_Set(fc->samplerate);	//设置采样率    
			SAIA_TX_DMA_Init(16);	
			
			memset(audiodev.saibuf1,0,fc->max_blocksize*4);
			memset(audiodev.saibuf2,0,fc->max_blocksize*4);
		}  
		//sai_tx_callback=flac_sai_dma_tx_callback;	//回调函数指向flac_sai_dma_tx_callback
		f_read(audiodev.file,buffer,fc->max_framesize,&br);//读取最大帧长数据		
		bytesleft=br;
		
		audio_start();					//开始播放  
		flactransferend=0;	
		flacwitchbuf=0;		
		
		fc->decoded0=(int*)decbuf0;		//解码数组0
		fc->decoded1=(int*)decbuf1;  	//解码数组1 
		flac_fptr=audiodev.file->fptr;	//记录当前的文件位置.
		while(bytesleft) 
		{   
			if(flacwitchbuf == 0)
			{
				HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf1, flacctrl->outsamples);
				flacwitchbuf = 1;
			}
			else
			{
				HAL_SAI_Transmit_DMA(&hsai_BlockA1, audiodev.saibuf2, flacctrl->outsamples);
				flacwitchbuf = 0;
			}
			
				
//			if(flac_fptr!=audiodev.file->fptr)//说明外部有进行文件快进/快退操作
//			{
//				if(audiodev.file->fptr<flacctrl->datastart)//在数据开始之前??
//				{
//					f_lseek(audiodev.file,flacctrl->datastart);//偏移到数据开始的地方
//				} 
//				f_read(audiodev.file,buffer,fc->max_framesize,&br); //读取一个最大帧的数据量
//				bytesleft=flac_seek_frame(buffer,br,fc);		//查找帧 
//				if(bytesleft>=0)								//找到正确的帧头.
//				{
//					f_lseek(audiodev.file,audiodev.file->fptr-fc->max_framesize+bytesleft);
//					f_read(audiodev.file,buffer,fc->max_framesize,&br); 
//				}else printf("flac seek error:%d\r\n",bytesleft); 
//				bytesleft=br;
//			}

			if(flacwitchbuf==0)p8=audiodev.saibuf1;
			else p8=audiodev.saibuf2; 
			if(fc->bps==24)res=flac_decode_frame24(fc,buffer,bytesleft,(s32*)p8);
			else res=flac_decode_frame16(fc,buffer,bytesleft,(s16*)p8); 
			if(res!=0)//解码出错了 
			{
				res=0xff;
				break;
			} 
			consumed=fc->gb.index/8;
			memmove(buffer,&buffer[consumed],bytesleft-consumed);
			bytesleft-=consumed; 
			res=f_read(audiodev.file,&buffer[bytesleft],fc->max_framesize-bytesleft,&br); 
			if(res)//读数据出错了
			{
				res=0xff;
				break;
			}
			if(br>0) 
			{
				bytesleft+=br;
			}
			flac_fptr=audiodev.file->fptr;	//记录当前的文件位置.
			
			while(flactransferend==0);//等待传输完成
			flactransferend = 0;
			
//			while(audiodev.status&(1<<1))	//正常播放中
//			{		  
//				flac_get_curtime(audiodev.file,flacctrl);//得到总时间和当前播放的时间 
//				audiodev.totsec=flacctrl->totsec;		//参数传递
//				audiodev.cursec=flacctrl->cursec;
//				audiodev.bitrate=flacctrl->bitrate;
//				audiodev.samplerate=flacctrl->samplerate;
//				audiodev.bps=flacctrl->bps;	
//  				if(audiodev.status&0X01)break;	//没有按下暂停 
//				//else delay_ms(1000/OS_TICKS_PER_SEC);
//			}
//			if((audiodev.status&(1<<1))==0)		//请求结束播放/播放完成
//			{  
//				break;
//			} 	 
			
			while(1)
			{
				key=KEY_Scan(0); 
				if(key==WKUP_PRES)//暂停
				{
					if(audiodev.status&0X01)audiodev.status&=~(1<<0);
					else audiodev.status|=0X01;  
				}
				if(key==KEY2_PRES||key==KEY0_PRES)//下一曲/上一曲
				{
					bytesleft = 0;
					res=key;
					break; 
				}
				flac_get_curtime(audiodev.file,flacctrl);//得到总时间和当前播放的时间 
				audiodev.totsec=flacctrl->totsec;		//参数传递
				audiodev.cursec=flacctrl->cursec;
				audiodev.bitrate=flacctrl->bitrate;
				audiodev.samplerate=flacctrl->samplerate;
				audiodev.bps=flacctrl->bps;	

				if((audiodev.status&0X01)==0)
				{
					delay_ms(10);
				}	
				else//正常播放
				{
					break;
				}							
			}
		} 
		audio_stop();
	}else res=0xff;
	f_close(audiodev.file);
	myfree(SRAMIN,fc);
	myfree(SRAMIN,flacctrl);
	myfree(SRAMIN,audiodev.file);
	myfree(SRAMIN,audiodev.saibuf1);
	myfree(SRAMIN,audiodev.saibuf2); 
	myfree(SRAMDTCM,buffer);
	myfree(SRAMDTCM,decbuf0);
	myfree(SRAMDTCM,decbuf1); 
  return res;
} 


#ifndef __BSP_FLACPLAY_H 
#define __BSP_FLACPLAY_H 

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

#include <inttypes.h>
#include <string.h>
#include "flacdecoder.h" 
#include "ff.h"  

//	 
//本程序移植自RockBox的flac解码库
//ALIENTEK STM32开发板
//FLAC 解码代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/6/29
//版本:V1.0
//********************************************************************************
//V1.0 说明
//1,支持16/24位单声道/立体声flac的解码
//2,最高支持192K/16bit或96K/24bit的flac解码  
// 	 

 
//flaC 标签 
typedef __packed struct 
{
    uint8_t id[3];		   	//ID,在文件起始位置,必须是flaC 4个字母 
}FLAC_Tag;

//metadata 数据块头信息结构体 
typedef __packed struct 
{
    uint8_t head;		   	//metadata block头
	uint8_t size[3];			//metadata block数据长度	
}MD_Block_Head;


//FLAC控制结构体
typedef __packed struct 
{ 
    uint32_t totsec ;				//整首歌时长,单位:秒
    uint32_t cursec ;				//当前播放时长
	
    uint32_t bitrate;	   			//比特率
	uint32_t samplerate;				//采样率
	uint16_t outsamples;				//PCM输出数据量大小
	uint16_t bps;					//位数,比如16bit,24bit,32bit
	
	uint32_t datastart;				//数据帧开始的位置(在文件里面的偏移)
}__flacctrl;

extern __flacctrl * flacctrl;


uint8_t flac_init(FIL* fx,__flacctrl* fctrl,FLACContext* fc);
void flac_i2s_dma_tx_callback(void);
void flac_get_curtime(FIL*fx,__flacctrl *flacx);
uint8_t flac_play_song(uint8_t* fname);

extern volatile uint8_t flactransferend;	//sai传输完成标志


#ifdef __cplusplus
}
#endif

#endif /* __BSP_FLACPLAY_H */



/*---------------------------------------------------------------------------/
/  FatFs Functional Configurations
/---------------------------------------------------------------------------*/

#define FFCONF_DEF	86631	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_READONLY	0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */


#define FF_FS_MINIMIZE	0
/* This option defines minimization level to remove some basic API functions.
/
/   0: Basic functions are fully enabled.
/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/      are removed.
/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/   3: f_lseek() function is removed in addition to 2. */


#define FF_USE_FIND		0
/* This option switches filtered directory read functions, f_findfirst() and
/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */


#define FF_USE_MKFS		1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */


#define FF_USE_FASTSEEK	1
/* This option switches fast seek function. (0:Disable or 1:Enable) */


#define FF_USE_EXPAND	0
/* This option switches f_expand function. (0:Disable or 1:Enable) */


#define FF_USE_CHMOD	0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */


#define FF_USE_LABEL	0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */


#define FF_USE_FORWARD	0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */


#define FF_USE_STRFUNC	1
#define FF_PRINT_LLI	0
#define FF_PRINT_FLOAT	0
#define FF_STRF_ENCODE	0
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/  f_printf().
/
/   0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/   1: Enable without LF-CRLF conversion.
/   2: Enable with LF-CRLF conversion.
/
/  FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
   makes f_printf() support floating point argument. These features want C99 or later.
/  When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/  encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/  to be read/written via those functions.
/
/   0: ANSI/OEM in current CP
/   1: Unicode in UTF-16LE
/   2: Unicode in UTF-16BE
/   3: Unicode in UTF-8
*/


/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define FF_CODE_PAGE	936
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect code page setting can cause a file open failure.
/
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
/     0 - Include all code pages above and configured by f_setcp()
*/


#define FF_USE_LFN		3
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */


#define FF_LFN_UNICODE	0
/* This option switches the character encoding on the API when LFN is enabled.
/
/   0: ANSI/OEM in current CP (TCHAR = char)
/   1: Unicode in UTF-16 (TCHAR = WCHAR)
/   2: Unicode in UTF-8 (TCHAR = char)
/   3: Unicode in UTF-32 (TCHAR = DWORD)
/
/  Also behavior of string I/O functions will be affected by this option.
/  When LFN is not enabled, this option has no effect. */


#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
/* This set of options defines size of file name members in the FILINFO structure
/  which is used to read out directory items. These values should be suffcient for
/  the file names to read. The maximum possible length of the read file name depends
/  on character encoding. When LFN is not enabled, these options have no effect. */


#define FF_FS_RPATH		0
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/


/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define FF_VOLUMES		1
/* Number of volumes (logical drives) to be used. (1-10) */


#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/  not defined, a user defined volume string table needs to be defined as:
/
/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/


#define FF_MULTI_PARTITION	0
/* This option switches support for multiple volumes on the physical drive.
/  By default (0), each logical drive number is bound to the same physical drive
/  number and only an FAT volume found on the physical drive will be mounted.
/  When this function is enabled (1), each logical drive number can be bound to
/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/  funciton will be available. */


#define FF_MIN_SS		512
#define FF_MAX_SS		512
/* This set of options configures the range of sector size to be supported. (512,
/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/  harddisk, but a larger value may be required for on-board flash memory and some
/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/  for variable sector size mode and disk_ioctl() function needs to implement
/  GET_SECTOR_SIZE command. */


#define FF_LBA64		0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */


#define FF_MIN_GPT		0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */


#define FF_USE_TRIM		0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/  To enable Trim function, also CTRL_TRIM command should be implemented to the
/  disk_ioctl() function. */



/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_TINY		0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/  Instead of private sector buffer eliminated from the file object, common sector
/  buffer in the filesystem object (FATFS) is used for the file data transfer. */


#define FF_FS_EXFAT		0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/  Note that enabling exFAT discards ANSI C (C89) compatibility. */


#define FF_FS_NORTC		0
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2020
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/  any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/  the timestamp function. Every object modified by FatFs will have a fixed timestamp
/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/  added to the project to read current time form real-time clock. FF_NORTC_MON,
/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */


#define FF_FS_NOFSINFO	0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/  option, and f_getfree() function at first time after volume mount will force
/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/  bit0=0: Use free cluster count in the FSINFO if available.
/  bit0=1: Do not trust free cluster count in the FSINFO.
/  bit1=0: Use last allocated cluster number in the FSINFO if available.
/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/


#define FF_FS_LOCK		0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/  is 1.
/
/  0:  Disable file lock function. To avoid volume corruption, application program
/      should avoid illegal open, remove and rename to the open objects.
/  >0: Enable file lock function. The value defines how many files/sub-directories
/      can be opened simultaneously under file lock control. Note that the file
/      lock control is independent of re-entrancy. */


/* #include <somertos.h>	// O/S definitions */
#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000
#define FF_SYNC_t		HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/  module itself. Note that regardless of this option, file access to different
/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/  and f_fdisk() function, are always not re-entrant. Only file/directory access
/  to the same volume is under control of this function.
/
/   0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/   1: Enable re-entrancy. Also user provided synchronization handlers,
/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/      function, must be added to the project. Samples are available in
/      option/syscall.c.
/
/  The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/  The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/  SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/  included somewhere in the scope of ff.h. */



/*--- End of configuration options ---*/

/* ***** BEGIN LICENSE BLOCK ***** 
 * Version: RCSL 1.0/RPSL 1.0 
 *  
 * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
 *      
 * The contents of this file, and the files included with this file, are 
 * subject to the current version of the RealNetworks Public Source License 
 * Version 1.0 (the "RPSL") available at 
 * http://www.helixcommunity.org/content/rpsl unless you have licensed 
 * the file under the RealNetworks Community Source License Version 1.0 
 * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
 * in which case the RCSL will apply. You may also obtain the license terms 
 * directly from RealNetworks.  You may not use this file except in 
 * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
 * applicable to this file, the RCSL.  Please see the applicable RPSL or 
 * RCSL for the rights, obligations and limitations governing use of the 
 * contents of the file.  
 *  
 * This file is part of the Helix DNA Technology. RealNetworks is the 
 * developer of the Original Code and owns the copyrights in the portions 
 * it created. 
 *  
 * This file, and the files included with this file, is distributed and made 
 * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
 * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
 * 
 * Technology Compatibility Kit Test Suite(s) Location: 
 *    http://www.helixcommunity.org/content/tck 
 * 
 * Contributor(s): 
 *  
 * ***** END LICENSE BLOCK ***** */ 

/**************************************************************************************
 * Fixed-point MP3 decoder
 * Jon Recker (jrecker@real.com), Ken Cooke (kenc@real.com)
 * June 2003
 *
 * buffers.c - allocation and freeing of internal MP3 decoder buffers
 *
 * All memory allocation for the codec is done in this file, so if you don't want 
 *  to use other the default system malloc() and free() for heap management this is 
 *  the only file you'll need to change.
 **************************************************************************************/

// J.Sz. 21/04/2006 #include "hlxclib/stdlib.h"		/* for malloc, free */ 


#include "coder.h"

//#define static_buffers
#ifdef static_buffers
MP3DecInfo  mp3DecInfo;     //  0x7f0 =  2032 
SubbandInfo sbi;            // 0x2204 =  8708
IMDCTInfo mi;               // 0x1b20 =  6944
HuffmanInfo hi;             // 0x1210 =  4624
DequantInfo di;             //  0x348 =   840
ScaleFactorInfo sfi;        //  0x124 =   292
SideInfo si;                //  0x148 =   328
FrameHeader fh;             //   0x38 =    56
#else
//#include <malloc.h> 
#include "bsp_malloc.h"
#endif

/**************************************************************************************
 * Function:    ClearBuffer
 *
 * Description: fill buffer with 0's
 *
 * Inputs:      pointer to buffer
 *              number of bytes to fill with 0
 *
 * Outputs:     cleared buffer
 *
 * Return:      none
 *
 * Notes:       slow, platform-independent equivalent to memset(buf, 0, nBytes)
 **************************************************************************************/
void ClearBuffer(void *buf, int nBytes)
{
	int i;
	unsigned char *cbuf = (unsigned char *)buf;

	for (i = 0; i < nBytes; i++)
		cbuf[i] = 0;

}

/**************************************************************************************
 * Function:    AllocateBuffers
 *
 * Description: allocate all the memory needed for the MP3 decoder
 *
 * Inputs:      none
 *
 * Outputs:     none
 *
 * Return:      pointer to MP3DecInfo structure (initialized with pointers to all 
 *                the internal buffers needed for decoding, all other members of 
 *                MP3DecInfo structure set to 0)
 *
 * Notes:       if one or more mallocs fail, function frees any buffers already
 *                allocated before returning
 *
 *              Changed by Kasper Jepsen to support static buffers as well.
 *
 **************************************************************************************/
MP3DecInfo *AllocateBuffers(void)
{
  MP3DecInfo *mp3DecInfo_pointer;
  #ifdef static_buffers
  mp3DecInfo_pointer = (MP3DecInfo*)&mp3DecInfo;
  ClearBuffer((void*)&mp3DecInfo, sizeof(MP3DecInfo));

  	mp3DecInfo.FrameHeaderPS =     (void*)&fh;
	mp3DecInfo.SideInfoPS =        (void*)&si;
	mp3DecInfo.ScaleFactorInfoPS = (void*)&sfi;
	mp3DecInfo.HuffmanInfoPS =     (void*)&hi;
	mp3DecInfo.DequantInfoPS =     (void*)&di;
	mp3DecInfo.IMDCTInfoPS =       (void*)&mi;
	mp3DecInfo.SubbandInfoPS =     (void*)&sbi;

	/* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */
    ClearBuffer((void*)&fh,  sizeof(FrameHeader));
	ClearBuffer((void*)&si,  sizeof(SideInfo));
	ClearBuffer((void*)&sfi, sizeof(ScaleFactorInfo));
	ClearBuffer((void*)&hi,  sizeof(HuffmanInfo));
	ClearBuffer((void*)&di,  sizeof(DequantInfo));
	ClearBuffer((void*)&mi,  sizeof(IMDCTInfo));
	ClearBuffer((void*)&sbi, sizeof(SubbandInfo));
   // return mp3DecInfo_pointer;

  #else
	FrameHeader *fh;
	SideInfo *si;
	ScaleFactorInfo *sfi;
	HuffmanInfo *hi;
	DequantInfo *di;
	IMDCTInfo *mi;
	SubbandInfo *sbi;

	mp3DecInfo_pointer = (MP3DecInfo *)mymalloc(SRAMIN,sizeof(MP3DecInfo));
	if (!mp3DecInfo_pointer)
		return 0;
	ClearBuffer(mp3DecInfo_pointer, sizeof(MP3DecInfo));
	
	fh =  (FrameHeader *)     mymalloc(SRAMIN,sizeof(FrameHeader));
	si =  (SideInfo *)        mymalloc(SRAMIN,sizeof(SideInfo));
	sfi = (ScaleFactorInfo *) mymalloc(SRAMIN,sizeof(ScaleFactorInfo));
	hi =  (HuffmanInfo *)     mymalloc(SRAMIN,sizeof(HuffmanInfo));
	di =  (DequantInfo *)     mymalloc(SRAMIN,sizeof(DequantInfo));
	mi =  (IMDCTInfo *)       mymalloc(SRAMIN,sizeof(IMDCTInfo));
	sbi = (SubbandInfo *)     mymalloc(SRAMIN,sizeof(SubbandInfo));

	mp3DecInfo_pointer->FrameHeaderPS =     (void *)fh;
	mp3DecInfo_pointer->SideInfoPS =        (void *)si;
	mp3DecInfo_pointer->ScaleFactorInfoPS = (void *)sfi;
	mp3DecInfo_pointer->HuffmanInfoPS =     (void *)hi;
	mp3DecInfo_pointer->DequantInfoPS =     (void *)di;
	mp3DecInfo_pointer->IMDCTInfoPS =       (void *)mi;
	mp3DecInfo_pointer->SubbandInfoPS =     (void *)sbi;

	if (!fh || !si || !sfi || !hi || !di || !mi || !sbi) {
		FreeBuffers(mp3DecInfo_pointer);	/* safe to call - only frees memory that was successfully allocated */
		return 0;
	}

	/* important to do this - DSP primitives assume a bunch of state variables are 0 on first use */
	//Optimized away.. hmm
    ClearBuffer(fh,  sizeof(FrameHeader));
	ClearBuffer(si,  sizeof(SideInfo));
	ClearBuffer(sfi, sizeof(ScaleFactorInfo));
	ClearBuffer(hi,  sizeof(HuffmanInfo));
	ClearBuffer(di,  sizeof(DequantInfo));
	ClearBuffer(mi,  sizeof(IMDCTInfo));
	ClearBuffer(sbi, sizeof(SubbandInfo));

#endif
	return mp3DecInfo_pointer;
}


#ifndef static_buffers
#define SAFE_FREE(x)	{if (x)	myfree(SRAMIN,x);	(x) = 0;}	/* helper macro */
#endif

/**************************************************************************************
 * Function:    FreeBuffers
 *
 * Description: frees all the memory used by the MP3 decoder
 *
 * Inputs:      pointer to initialized MP3DecInfo structure
 *
 * Outputs:     none
 *
 * Return:      none
 *
 * Notes:       safe to call even if some buffers were not allocated (uses SAFE_FREE)
 **************************************************************************************/
void FreeBuffers(MP3DecInfo *mp3DecInfo)
{
#ifndef static_buffers	
    if (!mp3DecInfo)
		return;

	SAFE_FREE(mp3DecInfo->FrameHeaderPS);
	SAFE_FREE(mp3DecInfo->SideInfoPS);
	SAFE_FREE(mp3DecInfo->ScaleFactorInfoPS);
	SAFE_FREE(mp3DecInfo->HuffmanInfoPS);
	SAFE_FREE(mp3DecInfo->DequantInfoPS);
	SAFE_FREE(mp3DecInfo->IMDCTInfoPS);
	SAFE_FREE(mp3DecInfo->SubbandInfoPS);

	SAFE_FREE(mp3DecInfo);
#endif
}

/*
 * Common bit i/o utils
 * Copyright (c) 2000, 2001 Fabrice Bellard.
 * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * alternative bitstream reader & writer by Michael Niedermayer <michaelni@gmx.at>
 */

/**
 * @file bitstream.c
 * bitstream api.
 */

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h> 
#include "bitstreamf.h"

/* bit input functions */

/** 
 * reads 0-32 bits.
 */
unsigned int get_bits_long(GetBitContext *s, int n){
    if(n<=17) return get_bits(s, n);
    else{
        int ret= get_bits(s, 16) << (n-16);
        return ret | get_bits(s, n-16);
    }
}

/** 
 * shows 0-32 bits.
 */
unsigned int show_bits_long(GetBitContext *s, int n){
    if(n<=17) return show_bits(s, n);
    else{
        GetBitContext gb= *s;
        int ret= get_bits_long(s, n);
        *s= gb;
        return ret;
    }
}

void align_get_bits(GetBitContext *s)
{
    int n= (-get_bits_count(s)) & 7;
    if(n) skip_bits(s, n);
}


/*
 * FLAC (Free Lossless Audio Codec) decoder
 * Copyright (c) 2003 Alex Beregszaszi
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/**
 * @file flac.c
 * FLAC (Free Lossless Audio Codec) decoder
 * @author Alex Beregszaszi
 *
 * For more information on the FLAC format, visit:
 *  http://flac.sourceforge.net/
 *
 * This decoder can be used in 1 of 2 ways: Either raw FLAC data can be fed
 * through, starting from the initial 'fLaC' signature; or by passing the
 * 34-byte streaminfo structure through avctx->extradata[_size] followed
 * by data starting with the 0xFFF8 marker.
 */

#include <inttypes.h>
#include <stdbool.h>

//#include "arm.h"
#include "golomb.h"
#include "flacdecoder.h"


#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))

static const int sample_rate_table[] ICONST_ATTR =
{ 0, 88200, 176400, 192000,
  8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
  0, 0, 0, 0 };

static const int sample_size_table[] ICONST_ATTR = 
{ 0, 8, 12, 0, 16, 20, 24, 0 };

static const int blocksize_table[] ICONST_ATTR = {
     0,    192, 576<<0, 576<<1, 576<<2, 576<<3,      0,      0, 
256<<0, 256<<1, 256<<2, 256<<3, 256<<4, 256<<5, 256<<6, 256<<7 
};

static const uint8_t table_crc8[256] ICONST_ATTR = {
    0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
    0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
    0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
    0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
    0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
    0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
    0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
    0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
    0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
    0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
    0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
    0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
    0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
    0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
    0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
    0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
    0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
    0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
    0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
    0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
    0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
    0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
    0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
    0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
    0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
    0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63,
    0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
    0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
    0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
    0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83,
    0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
    0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
};

static int64_t get_utf8(GetBitContext *gb) ICODE_ATTR_FLAC;
static int64_t get_utf8(GetBitContext *gb)
{
    uint64_t val;
    int ones=0, bytes;
    
    while(get_bits1(gb))
        ones++;

    if     (ones==0) bytes=0;
    else if(ones==1) return -1;
    else             bytes= ones - 1;
    
    val= get_bits(gb, 7-ones);
    while(bytes--){
        const int tmp = get_bits(gb, 8);
        
        if((tmp>>6) != 2)
            return -2;
        val<<=6;
        val|= tmp&0x3F;
    }
    return val;
}

static int get_crc8(const uint8_t *buf, int count) ICODE_ATTR_FLAC;
static int get_crc8(const uint8_t *buf, int count)
{
    int crc=0;
    int i;
    
    for(i=0; i<count; i++){
        crc = table_crc8[crc ^ buf[i]];
    }

    return crc;
}

static int decode_residuals(FLACContext *s, int32_t* decoded, int pred_order) ICODE_ATTR_FLAC;
static int decode_residuals(FLACContext *s, int32_t* decoded, int pred_order)
{
    int i, tmp, partition, method_type, rice_order;
    int sample = 0, samples;

    method_type = get_bits(&s->gb, 2);
    if (method_type > 1){
        //fprintf(stderr,"illegal residual coding method %d\n", method_type);
        return -3;
    }
    
    rice_order = get_bits(&s->gb, 4);

    samples= s->blocksize >> rice_order;

    sample= 
    i= pred_order;
    for (partition = 0; partition < (1 << rice_order); partition++)
    {
        tmp = get_bits(&s->gb, method_type == 0 ? 4 : 5);
        if (tmp == (method_type == 0 ? 15 : 31))
        {
            //fprintf(stderr,"fixed len partition\n");
            tmp = get_bits(&s->gb, 5);
            for (; i < samples; i++, sample++)
                decoded[sample] = get_sbits(&s->gb, tmp);
        }
        else
        {
            for (; i < samples; i++, sample++){
                decoded[sample] = get_sr_golomb_flac(&s->gb, tmp, INT_MAX, 0);
            }
        }
        i= 0;
    }

    return 0;
}    

static int decode_subframe_fixed(FLACContext *s, int32_t* decoded, int pred_order) ICODE_ATTR_FLAC;
static int decode_subframe_fixed(FLACContext *s, int32_t* decoded, int pred_order)
{
    const int blocksize = s->blocksize;
    int a, b, c, d, i;
        
    /* warm up samples */
    for (i = 0; i < pred_order; i++)
    {
        decoded[i] = get_sbits(&s->gb, s->curr_bps);
    }
    
    if (decode_residuals(s, decoded, pred_order) < 0)
        return -4;

    a = decoded[pred_order-1];
    b = a - decoded[pred_order-2];
    c = b - decoded[pred_order-2] + decoded[pred_order-3];
    d = c - decoded[pred_order-2] + 2*decoded[pred_order-3] - decoded[pred_order-4];

    switch(pred_order)
    {
        case 0:
            break;
        case 1:
            for (i = pred_order; i < blocksize; i++)
                decoded[i] = a += decoded[i];
            break;
        case 2:
            for (i = pred_order; i < blocksize; i++)
                decoded[i] = a += b += decoded[i];
            break;
        case 3:
            for (i = pred_order; i < blocksize; i++)
                decoded[i] = a += b += c += decoded[i];
            break;
        case 4:
            for (i = pred_order; i < blocksize; i++)
                decoded[i] = a += b += c += d += decoded[i];
            break;
        default:
            return -5;
    }

    return 0;
}


/* level8 用到这个函数最多
 * 之前版本经过验证是不行的
 * 这个函数有arm汇编版,但移植不成功
 * 找了个C语言版本的,效率不高
 */
int decode_subframe_lpc(FLACContext *s, int32_t* decoded, int pred_order)
{
     int i, j;
     int coeff_prec, qlevel;
     int coeffs[32];
 
     /* warm up samples */
     for (i = 0; i < pred_order; i++) {
         decoded[i] = get_sbits(&s->gb, s->curr_bps);
     }
 
     coeff_prec = get_bits(&s->gb, 4) + 1;
     if (coeff_prec == 16) {
//         av_log(s->avctx, AV_LOG_ERROR, "invalid coeff precision\n");
         return -1;
     }
     qlevel = get_sbits(&s->gb, 5);
     if (qlevel < 0) {
//         av_log(s->avctx, AV_LOG_ERROR, "qlevel %d not supported, maybe buggy stream\n",
//                qlevel);
         return -1;
     }
 
     for (i = 0; i < pred_order; i++) {
        coeffs[i] = get_sbits(&s->gb, coeff_prec);
     }
 
     if (decode_residuals(s, decoded, pred_order) < 0)
         return -1;
 
     if (s->bps > 16) {
         int64_t sum;
         for (i = pred_order; i < s->blocksize; i++) {
             sum = 0;
             for (j = 0; j < pred_order; j++)
                 sum += (int64_t)coeffs[j] * decoded[i-j-1];
             decoded[i] += sum >> qlevel;
         }
     } else {
         for (i = pred_order; i < s->blocksize-1; i += 2) {
             int c;
             int d = decoded[i-pred_order];
             int s0 = 0, s1 = 0;
             for (j = pred_order-1; j > 0; j--) {
                 c = coeffs[j];
                 s0 += c*d;
                 d = decoded[i-j];
                 s1 += c*d;
             }
             c = coeffs[0];
             s0 += c*d;
             d = decoded[i] += s0 >> qlevel;
             s1 += c*d;
             decoded[i+1] += s1 >> qlevel;
         }
         if (i < s->blocksize) {
             int sum = 0;
             for (j = 0; j < pred_order; j++)
                 sum += coeffs[j] * decoded[i-j-1];
             decoded[i] += sum >> qlevel;
         }
     }
 
     return 0;

}

static __inline int decode_subframe(FLACContext *s, int channel, int32_t* decoded)
{
    int type, wasted = 0;
    int i, tmp;
    
    s->curr_bps = s->bps;
    if(channel == 0){
        if(s->decorrelation == RIGHT_SIDE)
            s->curr_bps++;
    }else{
        if(s->decorrelation == LEFT_SIDE || s->decorrelation == MID_SIDE)
            s->curr_bps++;
    }

    if (get_bits1(&s->gb))
    {
        //fprintf(stderr,"invalid subframe padding\n");
        return -9;
    }
    type = get_bits(&s->gb, 6);
//    wasted = get_bits1(&s->gb);
    
//    if (wasted)
//    {
//        while (!get_bits1(&s->gb))
//            wasted++;
//        if (wasted)
//            wasted++;
//        s->curr_bps -= wasted;
//    }
#if 0
    wasted= 16 - av_log2(show_bits(&s->gb, 17));
    skip_bits(&s->gb, wasted+1);
    s->curr_bps -= wasted;
#else
    if (get_bits1(&s->gb))
    {
        wasted = 1;
        while (!get_bits1(&s->gb))
            wasted++;
        s->curr_bps -= wasted;
        //fprintf(stderr,"%d wasted bits\n", wasted);
    }
#endif
//FIXME use av_log2 for types
    if (type == 0)
    {
        //fprintf(stderr,"coding type: constant\n");
        tmp = get_sbits(&s->gb, s->curr_bps);
        for (i = 0; i < s->blocksize; i++)
            decoded[i] = tmp;
    }
    else if (type == 1)
    {
        //fprintf(stderr,"coding type: verbatim\n");
        for (i = 0; i < s->blocksize; i++)
            decoded[i] = get_sbits(&s->gb, s->curr_bps);
    }
    else if ((type >= 8) && (type <= 12))
    {
        //fprintf(stderr,"coding type: fixed\n");
        if (decode_subframe_fixed(s, decoded, type & ~0x8) < 0)
            return -10;
    }
    else if (type >= 32)
    {
        //fprintf(stderr,"coding type: lpc\n");
        if (decode_subframe_lpc(s, decoded, (type & ~0x20)+1) < 0)
            return -11;
    }
    else
    {
        //fprintf(stderr,"Unknown coding type: %d\n",type);
        return -12;
    }
        
    if (wasted)
    {
        int i;
        for (i = 0; i < s->blocksize; i++)
            decoded[i] <<= wasted;
    }

    return 0;
}

static int decode_frame(FLACContext *s) ICODE_ATTR_FLAC;
static int decode_frame(FLACContext *s){
	int blocksize_code, sample_rate_code, sample_size_code, assignment, crc8;
	int decorrelation, bps, blocksize, samplerate;
	int res;
    
    blocksize_code = get_bits(&s->gb, 4);

    sample_rate_code = get_bits(&s->gb, 4);
    
    assignment = get_bits(&s->gb, 4); /* channel assignment */
    if (assignment < 8 && s->channels == assignment+1)
        decorrelation = INDEPENDENT;
    else if (assignment >=8 && assignment < 11 && s->channels == 2)
        decorrelation = LEFT_SIDE + assignment - 8;
    else
    {
        return -13;
    }
        
    sample_size_code = get_bits(&s->gb, 3);
    if(sample_size_code == 0)
        bps= s->bps;
    else if((sample_size_code != 3) && (sample_size_code != 7))
        bps = sample_size_table[sample_size_code];
    else 
    {
        return -14;
    }

    if (get_bits1(&s->gb))
    {
        return -15;
    }

    /* Get the samplenumber of the first sample in this block */
    s->samplenumber=get_utf8(&s->gb);

    /* samplenumber actually contains the frame number for streams
       with a constant block size - so we multiply by blocksize to
       get the actual sample number */
    if (s->min_blocksize == s->max_blocksize) {
        s->samplenumber*=s->min_blocksize;
    }

#if 0    
    if (/*((blocksize_code == 6) || (blocksize_code == 7)) &&*/
        (s->min_blocksize != s->max_blocksize)){
    }else{
    }
#endif

    if (blocksize_code == 0)
        blocksize = s->min_blocksize;
    else if (blocksize_code == 6)
        blocksize = get_bits(&s->gb, 8)+1;
    else if (blocksize_code == 7)
        blocksize = get_bits(&s->gb, 16)+1;
    else 
        blocksize = blocksize_table[blocksize_code];

    if(blocksize > s->max_blocksize){
        return -16;
    }

    if (sample_rate_code == 0){
        samplerate= s->samplerate;
    }else if ((sample_rate_code > 0) && (sample_rate_code < 12))
        samplerate = sample_rate_table[sample_rate_code];
    else if (sample_rate_code == 12)
        samplerate = get_bits(&s->gb, 8) * 1000;
    else if (sample_rate_code == 13)
        samplerate = get_bits(&s->gb, 16);
    else if (sample_rate_code == 14)
        samplerate = get_bits(&s->gb, 16) * 10;
    else{
        return -17;
    }

    skip_bits(&s->gb, 8);
    crc8= get_crc8(s->gb.buffer, get_bits_count(&s->gb)/8);
    if(crc8){
        return -18;
    }
    
    s->blocksize    = blocksize;
    s->samplerate   = samplerate;
    s->bps          = bps;
    s->decorrelation= (enum decorrelation_type)decorrelation;

    /* subframes */
    if ((res=decode_subframe(s, 0, s->decoded0)) < 0){
    	return res-100;
    }


    if (s->channels==2) {
      if ((res=decode_subframe(s, 1, s->decoded1)) < 0){
    	  return res-200;
      }
    }
    
    align_get_bits(&s->gb);

    /* frame footer */
    skip_bits(&s->gb, 16); /* data crc */

    return 0;
} 
//查找下一帧起始地址
//buf:输入数组
//size:数组大小
//fc:flac解码容器
//返回值:-1,没有找到帧标志
//     其他,帧起始偏移量
int flac_seek_frame(uint8_t *buf,uint32_t size,FLACContext * fc)
{
	uint8_t *p;
	uint32_t i;
	uint32_t samplerate;
	uint32_t bps; 
	uint8_t seekok=0;
	p=buf;
	for(i=0;i<size-3;i++,p++)
	{ 
		if(p[0]==0XFF&&((p[1]&0XFC)==0XF8))//找到帧同步字(0XFFF8)
		{
			samplerate=p[2]&0X0F;				//采样率
			if(samplerate==0)samplerate=fc->samplerate;
			else if(samplerate>11)continue;
			else samplerate=sample_rate_table[samplerate]; 
			bps=sample_size_table[(p[3]&0X0F)>>1];//采样深度 16/24位
			if(samplerate==fc->samplerate&&bps==fc->bps)
			{
				seekok=1;
				break;
			}
		}
	}  
	if(seekok)return i;	//帧在数组里面的起始地址偏移量
	else return -1;		//没有找到
}
//解码一帧24位FLAC文件
//fc:flac解码容器
//buf:输入数组(读取到的数据)
//buf_size:输入数组大小
//wavbuf:输出音频PCM数组(32bit)
//返回值:0,解码成功
//    其他,错误
int flac_decode_frame24(FLACContext *fc, uint8_t *buf, int buf_size, s32 *wavbuf)
{
	int sampleCnt, *ch0, *ch1; 
	
	init_get_bits(&fc->gb, buf, buf_size*8);
	skip_bits(&fc->gb, 16); 
	if((sampleCnt=decode_frame(fc))<0)
	{
		fc->bitstream_size=0;
		fc->bitstream_index=0;
		return sampleCnt;
	} 
	fc->framesize = (get_bits_count(&fc->gb)+7)>>3; 
	sampleCnt = fc->blocksize;
	ch0=fc->decoded0;
	ch1=fc->decoded1;

	switch(fc->decorrelation)
	{
		case INDEPENDENT :
			if (fc->channels==1) 
			{
				do
				{
					*(wavbuf ++) = *ch0;
					*(wavbuf ++) = *(ch0 ++);
				}while(-- sampleCnt);
			}else 
			{
				do
				{
					*(wavbuf ++) = *(ch0 ++);
					*(wavbuf ++) = *(ch1 ++);
				}while(-- sampleCnt);
			}
			break;
		case LEFT_SIDE: 
			do
			{
				*(wavbuf ++) = *ch0;
				*(wavbuf ++) = *(ch0 ++) - *(ch1 ++);
			}while(-- sampleCnt);
			break;
		case RIGHT_SIDE: 
			do
			{
				*(wavbuf ++) = *(ch0 ++) + *ch1;
				*(wavbuf ++) = *(ch1 ++);
			}while(-- sampleCnt);
			break;
		case MID_SIDE: 
			do
			{
				int mid, side; 
				mid  = *(ch0 ++);
				side = *(ch1 ++);
				mid -= side>>1;   
				*(wavbuf ++) = (mid + side);
				*(wavbuf ++) = mid;
			}while(-- sampleCnt);
			break;
		default :
			do
			{
				*(wavbuf ++) = 0;
				*(wavbuf ++) = 0;
			}while(-- sampleCnt);
	} 
	return 0;
}
//解码一帧16位FLAC文件 
//fc:flac解码容器
//buf:输入数组(读取到的数据)
//buf_size:输入数组大小
//wavbuf:输出音频PCM数组(16bit)
//返回值:0,解码成功
//    其他,错误
int flac_decode_frame16(FLACContext *fc, uint8_t *buf, int buf_size, s16 *wavbuf)
{
	int sampleCnt, *ch0, *ch1; 
	
	init_get_bits(&fc->gb, buf, buf_size*8);
	skip_bits(&fc->gb, 16); 
	if((sampleCnt=decode_frame(fc))<0)
	{
		fc->bitstream_size=0;
		fc->bitstream_index=0;
		return sampleCnt;
	} 
	fc->framesize = (get_bits_count(&fc->gb)+7)>>3; 
	sampleCnt = fc->blocksize;
	ch0=fc->decoded0;
	ch1=fc->decoded1;

	switch(fc->decorrelation)
	{
		case INDEPENDENT :
			if (fc->channels==1) 
			{
				do
				{
					*(wavbuf ++) = *ch0;
					*(wavbuf ++) = *(ch0 ++);
				}while(-- sampleCnt);
			}else 
			{
				do
				{
					*(wavbuf ++) = *(ch0 ++);
					*(wavbuf ++) = *(ch1 ++);
				}while(-- sampleCnt);
			}
			break;
		case LEFT_SIDE: 
			do
			{
				*(wavbuf ++) = *ch0;
				*(wavbuf ++) = *(ch0 ++) - *(ch1 ++);
			}while(-- sampleCnt);
			break;
		case RIGHT_SIDE: 
			do
			{
				*(wavbuf ++) = *(ch0 ++) + *ch1;
				*(wavbuf ++) = *(ch1 ++);
			}while(-- sampleCnt);
			break;
		case MID_SIDE: 
			do
			{
				int mid, side; 
				mid  = *(ch0 ++);
				side = *(ch1 ++);
				mid -= side>>1;   
				*(wavbuf ++) = (mid + side);
				*(wavbuf ++) = mid;
			}while(-- sampleCnt);
			break;
		default :
			do
			{
				*(wavbuf ++) = 0;
				*(wavbuf ++) = 0;
			}while(-- sampleCnt);
	} 
	return 0;
}

#include <inttypes.h>

/* From ffmpeg - libavutil/common.h */
const uint8_t ff_log2_tab[256] = {
    0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
    5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
};

总结:
1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述
4、
在这里插入图片描述

5、屏蔽stm32f7xx_hal_msp.c中的void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai),在bsp_sai.c中添加void SAIA_TX_DMA_Init(uint8_t width)、void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)
6、在bsp_sai.c中添加void BSP_SAI1_Init(uint32_t datasize, uint32_t AudioFrequency)、uint8_t SAIA_SampleRate_Set(uint32_t samplerate)
7、将ff.c中的函数FRESULT dir_sdi (DIR* dp, DWORD ofs);去掉static,并在ff.h中声明,bsp_audioplay.c中需要用到
8、SAI初始化时需要用到uint8_t SAIA_SampleRate_Set(uint32_t samplerate),HAL库中对采样率的设置不准确,实测播放歌曲会变快一点
9、DMA的设置,如果数据为16位,就设置为HALFWORD,如果数据为32位,就设置为WORD,PeriphDataAlignment必须要这样设置,MemDataAlignment可以不用,但是最好保持一致
10、SAI不用使能中断,运行正常
11、DMA虽然是单缓冲,但是也采用了两个BUFFER,一个BUFFER在发送DMA时,另外一个BUFFER填充数据,这样播放流畅,如果DMA一次发送的数据量太小,速度跟不上会听到卡顿的声音
12、在void SAIA_TX_DMA_Init(uint8_t width)函数中HAL_DMA_Init(&hdma_sai1_a)前添加HAL_DMA_DeInit(&hdma_sai1_a);会导致无法播放音乐
13、SAI初始化时SAIA_TX_DMA_Init(); 应放在BSP_SAI1_Init();SAIA_SampleRate_Set();之后,如果放在前面会导致无法播放音乐
14、cubemx生成代码后只需要屏蔽stm32f7xx_hal_msp.c中的void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)、void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai)即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值