esp32- eps32_snow audio play wav和mp3播放(1)
參考
http://blog.sina.com.cn/s/blog_166bd652e0102xcz4.html
http://blog.csdn.net/zhangjikuan/article/details/48978627
http://blog.csdn.net/u012507643/article/details/50432635
https://www.amobbs.com/forum.php?mod=viewthread&tid=4307649
https://www.helixcommunity.org/projects/datatype/mp3dec
https://en.wikipedia.org/wiki/MP3
http://blog.csdn.net/fulinwsuafcie/article/details/8972346
http://www.cnblogs.com/gansc23/archive/2010/11/27/1889537.html
http://blog.csdn.net/sunshine1314/article/details/2514322
未完,請見後需文章
播放aplay_wav檔案
需要先解析wav的標頭檔案
typedef struct { char rld[4]; //riff 標誌符號 int rLen; // char wld[4]; //格式型別(wave) char fld[4]; //"fmt" int fLen; //sizeof(wave format matex) short wFormatTag; //編碼格式 short wChannels; //聲道數 int nSamplesPersec; //取樣頻率 int nAvgBitsPerSample;//WAVE檔案取樣大小 short wBlockAlign; //塊對齊 short wBitsPerSample; //WAVE檔案取樣大小 char dld[4]; //”data“ int wSampleLength; //音訊資料的大小 }WAV_HEADER;
來自http://blog.csdn.net/u012507643/article/details/50432635的圖片
隨後,把data部分讀取出來,直接塞給codec就行了
void aplay_wav(char* filename){ //"/sdcard/test.wav" WAV_HEADER wav_head; FILE *f= fopen(filename, "r"); if (f == NULL) { ESP_LOGE(TAG,"Failed to open file:%s",filename); return; } //fprintf(f, "Hello %s!\n", card->cid.name); int rlen=fread(&wav_head,1,sizeof(wav_head),f); if(rlen!=sizeof(wav_head)){ ESP_LOGE(TAG,"read faliled"); return; } int channels = wav_head.wChannels; int frequency = wav_head.nSamplesPersec; int bit = wav_head.wBitsPerSample; int datalen= wav_head.wSampleLength; (void)datalen; ESP_LOGI(TAG,"channels:%d,frequency:%d,bit:%d\n",channels,frequency,bit); char* samples_data = malloc(1024); do{ rlen=fread(samples_data,1,1024,f); //datalen-=rlen; hal_i2s_write(0,samples_data,rlen,5000); }while(rlen>0); fclose(f); free(samples_data); f=NULL; }
播放mp3
這裡使用的helix的解碼,helix的流程大致如下
來自http://blog.csdn.net/weixin_39871788/article/details/79330345的圖
申請快取後,先讀取10bytes的ID3的頭資訊,
“在檔案的首部順序記錄10 個位元組的ID3V2.3 的頭部。資料結構如下:
char Header[3]; /*必須為"ID3"否則認為標籤不存在*/
char Ver; /*版本號ID3V2.3 就記錄3*/
char Revision; /*副版本號此版本記錄為0*/
char Flag; /*存放標誌的位元組,這個版本只定義了三位,稍後詳細解說*/
char Size[4]; /*標籤大小,包括標籤頭的10 個位元組和所有的標籤幀的大小*/”
(http://blog.csdn.net/fulinwsuafcie/article/details/8972346)
如下,程式碼註釋
void aplay_mp3(char *path)
{
ESP_LOGI(TAG,"start to decode ...");
HMP3Decoder hMP3Decoder;
MP3FrameInfo mp3FrameInfo;
unsigned char *readBuf=malloc(MAINBUF_SIZE);
if(readBuf==NULL){
ESP_LOGE(TAG,"readBuf malloc failed");
return;
}
short *output=malloc(1153*4);
if(output==NULL){
free(readBuf);
ESP_LOGE(TAG,"outBuf malloc failed");
}
hMP3Decoder = MP3InitDecoder();
if (hMP3Decoder == 0){
free(readBuf);
free(output);
ESP_LOGE(TAG,"memory is not enough..");
}
int samplerate=0;
i2s_zero_dma_buffer(0);
FILE *mp3File=fopen( path,"rb");
if(mp3File==NULL){
MP3FreeDecoder(hMP3Decoder);
free(readBuf);
free(output);
ESP_LOGE(TAG,"open file failed");
}
char tag[10];
int tag_len = 0;
int read_bytes = fread(tag, 1, 10, mp3File);//讀取頭資訊,共10 byte
if(read_bytes == 10)
{
if (memcmp(tag,"ID3",3) == 0) //判斷是否是id3標籤
{
//計算標籤大小,一共四個位元組,但每個位元組只用7位,最高位不使用恆為0,0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx,計算要去掉0
tag_len = ((tag[6] & 0x7F)<< 21)|((tag[7] & 0x7F) << 14) | ((tag[8] & 0x7F) << 7) | (tag[9] & 0x7F);
// ESP_LOGI(TAG,"tag_len: %d %x %x %x %x", tag_len,tag[6],tag[7],tag[8],tag[9]);
fseek(mp3File, tag_len - 10, SEEK_SET);//指向檔案頭+tag_len-10的位置??
}
else
{
fseek(mp3File, 0, SEEK_SET);//指向檔案頭
}
}
unsigned char* input = &readBuf[0];
int bytesLeft = 0;
int outOfData = 0;
unsigned char* readPtr = readBuf;
while (1)
{
if (bytesLeft < MAINBUF_SIZE)
{
memmove(readBuf, readPtr, bytesLeft);
//讀取(MAINBUF_SIZE - bytesLeft)*1個byte到readBuf + bytesLeft中去
int br = fread(readBuf + bytesLeft, 1, MAINBUF_SIZE - bytesLeft, mp3File);
if ((br == 0)&&(bytesLeft==0)) break;
bytesLeft = bytesLeft + br;
readPtr = readBuf;
}
int offset = MP3FindSyncWord(readPtr, bytesLeft);
if (offset < 0)
{
ESP_LOGE(TAG,"MP3FindSyncWord not find");
bytesLeft=0;
continue;
}
else
{
readPtr += offset; //data start point
bytesLeft -= offset; //in buffer
int errs = MP3Decode(hMP3Decoder, &readPtr, &bytesLeft, output, 0);
if (errs != 0)
{
ESP_LOGE(TAG,"MP3Decode failed ,code is %d ",errs);
break;
}
MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);
if(samplerate!=mp3FrameInfo.samprate)
{
samplerate=mp3FrameInfo.samprate;
//hal_i2s_init(0,samplerate,16,mp3FrameInfo.nChans);
i2s_set_clk(0,samplerate,16,mp3FrameInfo.nChans);
//wm8978_samplerate_set(samplerate);
ESP_LOGI(TAG,"mp3file info---bitrate=%d,layer=%d,nChans=%d,samprate=%d,outputSamps=%d",mp3FrameInfo.bitrate,mp3FrameInfo.layer,mp3FrameInfo.nChans,mp3FrameInfo.samprate,mp3FrameInfo.outputSamps);
}
i2s_write_bytes(0,(const char*)output,mp3FrameInfo.outputSamps*2, 1000 / portTICK_RATE_MS);
}
}
i2s_zero_dma_buffer(0);
//i2s_driver_uninstall(0);
MP3FreeDecoder(hMP3Decoder);
free(readBuf);
free(output);
fclose(mp3File);
ESP_LOGI(TAG,"end mp3 decode ..");
}
以下來自 https://en.wikipedia.org/wiki/MP3
File structure
An MP3 file is made up of MP3 frames, which consist of a header and a data block. This sequence of frames is called an elementary stream. Due to the "byte reservoir", frames are not independent items and cannot usually be extracted on arbitrary frame boundaries. The MP3 Data blocks contain the (compressed) audio information in terms of frequencies and amplitudes. The diagram shows that the MP3 Header consists of a sync word, which is used to identify the beginning of a valid frame. This is followed by a bit indicating that this is the MPEG standard and two bits that indicate that layer 3 is used; hence MPEG-1 Audio Layer 3 or MP3. After this, the values will differ, depending on the MP3 file. ISO/IEC 11172-3 defines the range of values for each section of the header along with the specification of the header. Most MP3 files today contain ID3 metadata, which precedes or follows the MP3 frames, as noted in the diagram. The data stream can contain an optional checksum.
以下介紹:https://www.helixcommunity.org/projects/datatype/mp3dec
The Helix MP3 Decoder
Key Features- Pure 32-bit fixed-point implementation
- High-quality C reference code for porting to new platforms
- Optimized for ARM processors
- Fully reentrant and statically linkable
- Optional C++ API for compatibility with Helix clients
- Designed for high performance and low power consumption in handheld and mobile devices
- Full layer 3 support for
- MPEG1 layer 3 - sampling frequencies: 48 KHz, 44.1 KHz, 32 KHz
- MPEG2 layer 3 - sampling frequencies: 24 KHz, 22.05 KHz, 16 KHz
- MPEG2.5 layer 3 - sampling frequencies: 12 KHz, 11.025 KHz, 8 KHz
- Supports constant bitrate, variable bitrate, and free bitrate modes
- Supports mono and all stereo modes (normal stereo, joint stereo, dual-mono)
- Option to use Intel® IPP performance libraries (if available)
- Easy to link in either IPP libraries or Helix code
Average CPU Usage
- ROM = 13446 Bytes (const globals)
- RAM = 23816 Bytes (heap)
- Total Data Memory = 37262 Bytes
- Code Size = 21000 Bytes (approximately - depends on compiler)
Where is the code in CVS?
- See the Helix Datatype project page: http://datatype.helixcommunity.org
- The CVS root is /cvsroot/datatype, and the module name (path) is mp3/codec/fixpt
- datatype/mp3/codec/fixpt
- If HELIX_CONFIG_FIXEDPOINT is defined in your profile, it will build the fixed-point decoder. Otherwise it will build the floating-point version.
The Helix MP3 decoder provides MPEG-compliant decoding of MP3 content. Both floating-point and fixed-point decoder implementations are available. The fixed-point decoder is optimized especially for ARM processors but can run on any 32-bit fixed-point processor which can perform a long multiply operation (two 32-bit inputs generating a 64-bit result) and long multiply-accumulate (long multiply with 64-bit accumulator).
(1) Tested with ARMulator, simulated zero-wait-state memory