sdk-ameba-v4.0c_180328/component/common/example/audio/example_audio.c
2019-04-02 16:34:25 +08:00

359 lines
9.8 KiB
C
Executable file

#include "example_audio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "diag.h"
#include "i2s_api.h"
#include "analogin_api.h"
#include <platform/platform_stdlib.h>
#include "sgtl5000.h"
#define CONFIG_PLAY_SD_WAV 0 // 1: play wav audio file store on SD card
//0: play audio file store on memory
#define CONFIG_TUNE_VOLUME 0 // tune volume by trimmer resistor and ADC
#define I2S_DMA_PAGE_SIZE 512 // 2 ~ 4096
#define I2S_DMA_PAGE_NUM 4 // Vaild number is 2~4
/* play wav file on SD card */
#if CONFIG_PLAY_SD_WAV
#include "ff.h"
#include <fatfs_ext/inc/ff_driver.h>
#include <disk_if/inc/sdcard.h>
#include "wav.h"
SRAM_BF_DATA_SECTION u8 WAV_Buf[I2S_DMA_PAGE_SIZE];
#define SAMPLE_BLOCKS (I2S_DMA_PAGE_SIZE/2)
#else
#include "birds_44100_2ch_16b.c"
#endif
u8 i2s_tx_buf[I2S_DMA_PAGE_SIZE*I2S_DMA_PAGE_NUM];
u8 i2s_rx_buf[I2S_DMA_PAGE_SIZE*I2S_DMA_PAGE_NUM];
#if defined(CONFIG_PLATFORM_8195A)
#define I2S_SCLK_PIN PC_1
#define I2S_WS_PIN PC_0
#define I2S_SD_TX_PIN PC_2
#define I2S_SD_RX_PIN PC_4
#define I2S_MCK_PIN PC_3
#elif defined(CONFIG_PLATFORM_8711B)
#define I2S_SCLK_PIN PA_21
#define I2S_WS_PIN PA_22
#define I2S_SD_TX_PIN PA_19
#define I2S_SD_RX_PIN PA_20
#define I2S_MCK_PIN PA_18
#endif
i2s_t i2s_obj;
float volume_ratio = 0; // codec volume ration. 0.0 ~1.0
void i2s_tx_complete(void *data, char *pbuf)
{
//
}
void i2s_rx_complete(void *data, char* pbuf)
{
//
}
#if CONFIG_PLAY_SD_WAV
void print_wav_header(WavHeader *pwavHeader){
u8 buf[5];
printf("================wav header===============\n");
memcpy(buf, &pwavHeader->chunk_id[0], 4);
buf[4] = 0;
printf(" chunk_id: %s\n", buf);
printf(" chunk_size: 0x%08X\n",pwavHeader->chunk_size);
memcpy(buf, &pwavHeader->format[0], 4);
buf[4] = 0;
printf(" format: %s\n", buf);
memcpy(buf, &pwavHeader->fmtchunk_id[0], 4);
buf[4] = 0;
printf(" fmtchunk_id: %s\n", buf);
printf(" fmtchunk_size: 0x%08X\n",pwavHeader->fmtchunk_size);
printf(" audio_format: 0x%04X\n",pwavHeader->audio_format);
printf(" num_channels: 0x%04X\n",pwavHeader->num_channels);
printf(" sample_rate: 0x%08X\n",pwavHeader->sample_rate);
printf(" byte_rate: 0x%08X\n",pwavHeader->byte_rate);
printf(" block_align: 0x%04X\n",pwavHeader->block_align);
printf(" bps: 0x%04X\n",pwavHeader->bps);
memcpy(buf, &pwavHeader->datachunk_id[0], 4);
buf[4] = 0;
printf(" datachunk_id: %s\n", buf);
printf(" datachunk_size: 0x%08X\n",pwavHeader->datachunk_size);
printf("==================end====================\n");
}
void audio_play_sd_wav(u8* filename){
int drv_num = 0;
u32 read_length = 0;
FRESULT res;
FATFS m_fs;
FIL m_file;
char logical_drv[4]; /* root diretor */
char abs_path[64];
WavHeader pwavHeader;
u32 wav_length = 0;
u32 wav_offset = 0;
int *ptx_buf;
drv_num = FATFS_RegisterDiskDriver(&SD_disk_Driver);
if(drv_num < 0){
printf("Rigester disk driver to FATFS fail.\n");
return;
}else{
logical_drv[0] = drv_num + '0';
logical_drv[1] = ':';
logical_drv[2] = '/';
logical_drv[3] = 0;
}
if(f_mount(&m_fs, logical_drv, 1)!= FR_OK){
printf("FATFS mount logical drive fail, please format DISK to FAT16/32.\n");
goto unreg;
}
memset(abs_path, 0x00, sizeof(abs_path));
strcpy(abs_path, logical_drv);
sprintf(&abs_path[strlen(abs_path)],"%s", filename);
//Open source file
res = f_open(&m_file, abs_path, FA_OPEN_EXISTING | FA_READ); // open read only file
if(res != FR_OK){
printf("Open source file %(s) fail.\n", filename);
goto umount;
}
/* Read WAV header */
res = f_read(&m_file, WAV_Buf, sizeof(WavHeader),(UINT*)&read_length);
if((res != FR_OK) || (read_length == 0)){
printf("Read wav header fail!\n");
}
memcpy(&pwavHeader, WAV_Buf, sizeof(WavHeader));
print_wav_header(&pwavHeader);
if (strncmp(pwavHeader.chunk_id, "RIFF", 4) ||
strncmp(pwavHeader.format, "WAVE", 4)){
printf("Not a wav file\n");
goto exit;
}
/* only support PCM, 44.1K stereo wav file for this demo */
if (pwavHeader.audio_format != 1 ||
pwavHeader.sample_rate != 44100 ||
pwavHeader.num_channels != 2){
printf("Only PCM encoding 44.1K stereo wav file\n");
goto exit;
}
/* Read wav data length */
res = f_lseek(&m_file, m_file.fptr + pwavHeader.datachunk_size);
res = f_read(&m_file, WAV_Buf, 8,(UINT*)&read_length);
if((res != FR_OK) || (read_length == 0)){
printf("Read wav data info!\n");
}
if(strncmp(WAV_Buf, "data", 4)){
printf("Have not found invalid data chunk\n");
goto exit;
}
wav_length = *(u32*)&WAV_Buf[4];
printf("Audio data total length 0x%08X\n", wav_length);
do{
/* Read a block */
res = f_read(&m_file, WAV_Buf, I2S_DMA_PAGE_SIZE,(UINT*)&read_length);
if((res != FR_OK) || (read_length == 0)){
printf("Wav play done !\n");
break;
}
retry:
ptx_buf = i2s_get_tx_page(&i2s_obj);
if(ptx_buf){
if(read_length < I2S_DMA_PAGE_SIZE){
/* if valid audio data short than a page, make sure the rest of the page set to 0*/
memset((void*)ptx_buf, 0x00, I2S_DMA_PAGE_SIZE);
memcpy((void*)ptx_buf, (void*)&WAV_Buf[0], read_length);
}else
memcpy((void*)ptx_buf, (void*)&WAV_Buf[0], I2S_DMA_PAGE_SIZE);
i2s_send_page(&i2s_obj, (uint32_t*)ptx_buf);
}else{
vTaskDelay(1);
goto retry;
}
wav_offset += read_length;
}while(1);
printf("I2S write data length 0x%08X\n", wav_offset);
exit:
// close source file
res = f_close(&m_file);
if(res){
printf("close file (%s) fail.\n", filename);
}
umount:
if(f_mount(NULL, logical_drv, 1) != FR_OK){
printf("FATFS unmount logical drive fail.\n");
}
unreg:
if(FATFS_UnRegisterDiskDriver(drv_num))
printf("Unregister disk driver from FATFS fail.\n");
}
#else
/* only for 16bit sampled audio data */
void audio_play_memory(void *audio, u32 length){
int *ptx_buf;
s16 *p = audio;
u32 offset = 0;
u32 sampleBlocks = length*2; // 2 channel
u32 MaxBlocksPerCycle = I2S_DMA_PAGE_SIZE/sizeof(short);
u32 dataLen = 0;
again:
ptx_buf = i2s_get_tx_page(&i2s_obj);
if (ptx_buf) {
if(sampleBlocks-offset >= MaxBlocksPerCycle)
dataLen = MaxBlocksPerCycle*sizeof(short);
else{
/* if valid audio data short than a page, make sure the rest of the page set to 0*/
memset((void*)ptx_buf, 0x00, I2S_DMA_PAGE_SIZE);
dataLen = (sampleBlocks-offset)*sizeof(short);
}
_memcpy((void*)ptx_buf, (void*)(p+offset), dataLen);
i2s_send_page(&i2s_obj, (uint32_t*)ptx_buf);
offset += dataLen/sizeof(short);
if(offset >= sampleBlocks) {
offset = 0;
return;
}
goto again;
}else{
vTaskDelay(1);
goto again;
}
}
#endif
#if CONFIG_TUNE_VOLUME
#define MBED_ADC_PIN_3 AD_3 // HDK, A2
/*
* OFFSET: value of measuring at 0.000v, value(0.000v)
* GAIN_DIV: value(1.000v)-value(0.000v) or value(2.000v)-value(1.000v) or value(3.000v)-value(2.000v)
*
* MSB 12bit of value is valid, need to truncate LSB 4bit (0xABCD -> 0xABC). OFFSET and GAIN_DIV are truncated values.
*/
#define OFFSET 0x298
#define GAIN_DIV 0x34C
#define AD2MV(ad,offset,gain) (((ad/16)-offset)*1000/gain)
analogin_t adc2;
void audio_tune_volume_thread(void* param){
uint16_t offset, gain;
uint16_t adcdat2 = 0;
int32_t v_mv2;
float ratio = 0;
analogin_init(&adc2, MBED_ADC_PIN_3);
offset = OFFSET;
gain = GAIN_DIV;
while(1){
adcdat2 = analogin_read_u16(&adc2);
v_mv2 = AD2MV(adcdat2, offset, gain);
ratio = (float)v_mv2/(float)3300;
ratio = (float)((int)(ratio*10))/10.0;
if(ratio > 1.0)ratio = 1.0;
if(ratio < 0.0)ratio = 0.0;
if(ratio != volume_ratio){
volume_ratio = ratio;
#if CONFIG_EXAMPLE_CODEC_SGTL5000
sgtl5000_setVolume(volume_ratio);
#endif
}
vTaskDelay(100);
}
exit:
analogin_deinit(&adc2);
vTaskDelete(NULL);
}
#endif
void example_audio_thread(void* param){
printf("Audio codec demo begin......\n");
//Init I2S first, generate MCLK to drive SGTL5000
i2s_obj.channel_num = CH_STEREO;
i2s_obj.sampling_rate = SR_44p1KHZ;
i2s_obj.word_length = WL_16b;
i2s_obj.direction = I2S_DIR_TXRX;
i2s_init(&i2s_obj, I2S_SCLK_PIN, I2S_WS_PIN, I2S_SD_TX_PIN, I2S_SD_RX_PIN, I2S_MCK_PIN);
i2s_set_dma_buffer(&i2s_obj, (char*)i2s_tx_buf, (char*)i2s_rx_buf, \
I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE);
i2s_tx_irq_handler(&i2s_obj, (i2s_irq_handler)i2s_tx_complete, (uint32_t)&i2s_obj);
i2s_rx_irq_handler(&i2s_obj, (i2s_irq_handler)i2s_rx_complete, (uint32_t)&i2s_obj);
// i2s_recv_page(&i2s_obj); // sumbit a page to receive
#if CONFIG_EXAMPLE_CODEC_SGTL5000
sgtl5000_enable();
#endif
#if CONFIG_EXAMPLE_CODEC_ALC5651
alc5651_init();
alc5651_init_interface2();
#endif
#if CONFIG_TUNE_VOLUME
/*Create a task to tune codec volume*/
if(xTaskCreate(audio_tune_volume_thread, ((const char*)"audio_tune_volume_thread"), 2048, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
printf("\n\r%s xTaskCreate(audio_tune_volume_thread) failed", __FUNCTION__);
#endif
#if CONFIG_PLAY_SD_WAV
char wav[20] = "AudioSDTest.wav";
volume_ratio = 0.3;
#if CONFIG_EXAMPLE_CODEC_SGTL5000
sgtl5000_setVolume(volume_ratio);
#endif
printf("\nPlay %s on SD card.\n", wav);
audio_play_sd_wav(wav);
#else
volume_ratio = 0.7;
#if CONFIG_EXAMPLE_CODEC_SGTL5000
sgtl5000_setVolume(volume_ratio);
#endif
printf("\nPlay bird sing on memory.\n");
while(1){
audio_play_memory(birds_sing, birds_sing_size);
wait_ms(1000);
}
#endif
exit:
vTaskDelete(NULL);
}
void example_audio(void)
{
if(xTaskCreate(example_audio_thread, ((const char*)"example_audio_thread"), 2048, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS)
printf("\n\r%s xTaskCreate(example_audio_thread) failed", __FUNCTION__);
}