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


MicroPython is a great way to program the ESP32 to play MP3 files. Here's a simple example of how to do it: ``` import machine import time import uos # Define the pin numbers BCLK_PIN = 32 DOUT_PIN = 33 # Set up the I2S interface i2s = machine.I2S( mode=machine.I2S.MASTER_TX, bck=BCLK_PIN, ws=0, sd=DOUT_PIN, # Set the sample rate to 44100 Hz # This is the standard sample rate for MP3 files # You can change it to match the sample rate of your MP3 file # but keep in mind that not all sample rates are supported # by the ESP32's I2S interface standard=machine.I2S.PHILIPS, dataformat=machine.I2S.B32, samplerate=44100, # Set the number of channels to 2 (stereo) # This is the standard number of channels for MP3 files # You can change it to match the number of channels in your MP3 file # but keep in mind that not all numbers of channels are supported # by the ESP32's I2S interface channelformat=machine.I2S.RIGHT_LEFT, ) # Define the MP3 file path FILE_PATH = "/flash/song.mp3" # Open the MP3 file file = open(FILE_PATH, "rb") # Read the MP3 file header to determine the length of the file file.seek(0) header = file.read(4) file.seek(0) file_size = uos.stat(FILE_PATH)[6] length = int((file_size - 4) / 410) # Loop through the MP3 file and play it for i in range(length): # Read a chunk of data from the MP3 file data = file.read(410) # Write the data to the I2S interface i2s.write(data) # Wait a short amount of time between chunks time.sleep_ms(1) # Close the MP3 file file.close() # Stop the I2S interface i2s.stop() ``` This code sets up the ESP32's I2S interface to play an MP3 file, reads the MP3 file from flash memory, and writes the data to the I2S interface. It loops through the file and plays it in chunks, waiting a short amount of time between each chunk. When it's done playing the file, it stops the I2S interface.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值