1. 程式人生 > >esp32- eps32_snow audio play wav和mp3播放(1)

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
Technical Specifications
Average CPU Usage
Sample Rate
Channels
Bit Rate
Processor Model (1)
ARM7TDMI
ARM9TDMI-REV2
ARM920T
ARM9E
StrongARM1
XScale
48.0 KHz
2
320 Kbps
30 MHz
24 MHz
27 MHz
20 MHz
20 MHz
20 MHz
44.1 KHz
2
128 Kbps
26 MHz
21 MHz
24 MHz
17 MHz
17 MHz
17 MHz
Memory Usage
  • ROM = 13446 Bytes (const globals)
  • RAM = 23816 Bytes (heap)
  • Total Data Memory = 37262 Bytes
  • Code Size = 21000 Bytes (approximately - depends on compiler)
Frequently Asked Questions
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
Where does the build system put the code in my local source tree?
  • datatype/mp3/codec/fixpt
How does the build system decide whether to build the fixed-point or floating-point MP3 decoder?
  • 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