mirror of
https://github.com/jialexd/sdk-ameba-v4.0c_180328.git
synced 2024-11-24 23:14:18 +00:00
360 lines
9.8 KiB
C
360 lines
9.8 KiB
C
|
#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__);
|
||
|
}
|
||
|
|