问题描述
我担心使用 ESP32 WROOM 将来自 2 个 I2S MEMS 麦克风的音频数据存储到 SD 卡,但似乎没有运气这样做...
我的想法: 通过轮询 espressif I2S-API 提供的中断标志(在 DMA 缓冲区已满时激活),我触发了 I2S_read 调用并将获取的数据保存到更大的缓冲区。随着时间的推移,这个缓冲区被填满并被传送到 SD 卡,一个 wav 头已经在那里等待数据。标题中的长度信息由最后一个写入任务操作,当按下记录按钮时,该任务从操作系统时间函数millis() 和按下按钮时的另一个调用millis() 的减法中获取其信息再次。
我的资源:
- ESP32 WROOM
- 2 个 ADAFRUIT SPH0645 MEMS 麦克风(I2S 立体声)
- ADAFRUIT 微型 SD 卡适配器 (SPI)
- 外部中断按钮
- platformIO
- freeRTOS
- 采样率 44100 Hz
- 位深度 32 位
- 立体声
我的问题: 除了数据丢失和其他噪音外,还有一个更让我担心的问题:我的文件代表的声音速度提高了两倍,但不是音调的两倍,这意味着它不应该只是一个 wav-标题问题。查看 HxD 中的标题会显示标题中的正确数字,因此我的采样有一个基本缺陷,我没有看到。正如我所说,采样充满噪音,但这应该可以通过巧妙的缓冲区调整来解决。然而,时间问题不是。有人能够发现这个基本问题吗?我确定它只是在某个地方改变了一个数字,但在我看来,一切都应该计算......
我尝试了什么: 改变采样率或切换到单声道确实会改变音高,但速度似乎随之线性变化,总是比预期的两倍,这表明这确实不是标题问题。即使在从超级循环到基于任务的代码完全重做之后,仍然存在的一个错误是双速问题,这对我来说很奇怪。
我的代码:
这个任务在大数组中读取和存储数据以供进一步处理
void readI2STask(void* param){
//DEBUG
digitalWrite(WHITE_LED_PIN,LOW);
//init I2S
I2S_Init(I2S_MODE_RX,I2S_BITS_PER_SAMPLE_32BIT);
//fix for SPH645-specific timing-error:
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0),BIT(9));
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0),I2S_RX_MSB_SHIFT);
//index for globally accessible array
uint16_t bigBufferIndex = 0;
while(!powerOff_flag){
i2s_event_t evt;
//poll for ready-flag in queue
if (xQueueReceive(_i2sSampleQueue,&evt,portMAX_DELAY) == pdPASS){
if (evt.type == I2S_EVENT_RX_DONE){
size_t bytesRead = 0;
do{
uint8_t i2sData[SMALL_BUF_LEN];
i2s_read(I2S_NUM_0,i2sData,SMALL_BUF_LEN,&bytesRead,10);
int32_t* samples = (int32_t*)i2sData;
for (int i = 0; i < bytesRead / 4; i++){
SDBufMem1[bigBufferIndex] = samples[i];
bigBufferIndex++;
if (bigBufferIndex == SD_BUF_LEN_32BIT){
std::swap(SDBufMem1,SDBufMem2);
bigBufferIndex = 0;
bufFlag = (!bufFlag); //shows other functions which buffer is ready
if(recButton_flag){
xTaskNotify(_sendToSDTask,1,eIncrement);
}
else{
//xTaskNotify(_LCD_displayVUTask,eIncrement);
}
}
}
} while (bytesRead > 0);
}
}
}
}
此任务将可用数据存储到 SD
void sendToSDTask(void* param){
vTaskSuspend(_LCD_displayVUTask);
static char filename[] = "/rec000.wav";
const int headerSize = 512;
//default file-time = 1 second
const int waveDataSize = 2 * 44100 * 4;
byte header[headerSize];
File file;
const TickType_t xMaxBlockTime = pdMS_TO_TICKS(100);
//create RIFF-WAV-Header
CreateWavHeader(header,waveDataSize);
//find valid name
findName(filename);
//open/make file on SD card with given name
file = SD.open(filename,FILE_WRITE);
if(!file){
//Todo: exception handler for abrupt disconnection of SD-card before recording
}
//induce wav-header into start of file
file.write(header,headerSize);
//red means recording!
digitalWrite(RED_LED_PIN,HIGH);
unsigned long startTime = millis();
unsigned long endTime;
while (recButton_flag)
{
//wait for notofication from 16k array
uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE,xMaxBlockTime);
if (ulNotificationValue > 0)
{
if(bufFlag){
//1st buffer ready
file.write((const uint8_t*)SDBufMem1,(size_t)(SD_BUF_LEN_32BIT));
}
else{
//2nd buffer ready
file.write((const uint8_t*)SDBufMem2,(size_t)(SD_BUF_LEN_32BIT));
}
}
}
//calculate recording-lenght and save it to header at the start of the file
endTime = millis();
digitalWrite(RED_LED_PIN,LOW);
CreateWavHeader(header,((endTime-startTime) * 2* 4 * 44100 / 1000));
file.seek(0);
file.write(header,headerSize);
file.close();
vTaskResume(_LCD_displayVUTask);
vTaskDelete(NULL);
}
wav-header-creater
void CreateWavHeader(byte* header,int waveDataSize){
header[0] = 'R';
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
unsigned int fileSizeMinus8 = waveDataSize + 512 - 8;
header[4] = (byte)(fileSizeMinus8 & 0xFF);
header[5] = (byte)((fileSizeMinus8 >> 8) & 0xFF);
header[6] = (byte)((fileSizeMinus8 >> 16) & 0xFF);
header[7] = (byte)((fileSizeMinus8 >> 24) & 0xFF);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f';
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 0x10; // fmt lenght = 16byte
header[17] = 0x00; //
header[18] = 0x00; //
header[19] = 0x00; //
header[20] = 0x01; // format tag = 1 = PCM
header[21] = 0x00; //
header[22] = 0x02; // channels = 2 = stereo
header[23] = 0x00; //
header[24] = 0x44; // sampling rate = 44100
header[25] = 0xAC; //
header[26] = 0x00; //
header[27] = 0x00; //
header[28] = 0x20; // Byte/sec = 44100 * 2 * 4 = 352800
header[29] = 0x62; //
header[30] = 0x05; //
header[31] = 0x00; //
header[32] = 0x08; // block align (n channels = 2 * (bits/sample of one channel = 32 + 7)/8) = 8
header[33] = 0x00; //
header[34] = 0x20; // bits/sample = 32
header[35] = 0x00; //
header[36] = 'p'; //new padding sub-chunk. Increases SD-performance since it writes in 512 byte sized blocks naturally
header[37] = 'a'; //
header[38] = 'd'; //
header[39] = 'd'; //
header[40] = 0xCC; //padlen is rest of header - 512 = 460
header[41] = 0x01; //
header[42] = 0x00; //
header[43] = 0x00; //
for(int i = 44; i < 504; i++){
header[i] = 0x00;
}
header[504] = 'd'; //data sub-chunk
header[505] = 'a'; //
header[506] = 't'; //
header[507] = 'a'; //
header[508] = (byte)(waveDataSize & 0xFF);
header[509] = (byte)((waveDataSize >> 8) & 0xFF);
header[510] = (byte)((waveDataSize >> 16) & 0xFF);
header[511] = (byte)((waveDataSize >> 24) & 0xFF);
//real samples get appended here
}
I2S 初始化函数
void I2S_Init(i2s_mode_t MODE,i2s_bits_per_sample_t BPS)
{
//general config of I2S
//const: 44100 sample rate,stereo
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | MODE),.sample_rate = SAMPLE_RATE,.bits_per_sample = BPS,.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S),.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,.dma_buf_count = 4,.dma_buf_len = 1024,.use_apll = false,.tx_desc_auto_clear = false,.fixed_mclk = 0
};
//pin format,adjust in i2s.h if needed:
i2s_pin_config_t pin_config = {
.bck_io_num = PIN_I2S_BCLK,.ws_io_num = PIN_I2S_LRC,};
//i tried putting this into statement above,threw an error (?)
pin_config.data_in_num = PIN_I2S_DIN;
//init I2S-driver/peripherals
i2s_driver_install(I2S_NUM_0,&i2s_config,8,&_i2sSampleQueue);
i2s_set_pin(I2S_NUM_0,&pin_config);
i2s_set_clk(I2S_NUM_0,SAMPLE_RATE,BPS,I2S_CHANNEL_STEREO);
}
感谢您的帮助,非常感谢。 ~杰克
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)