|
本帖最后由 vany5921 于 2020-5-19 15:59 编辑
M5StickC内部自带了一个PDM制式麦克风数据精度12位,数据结构为16位,可以通过配置I2S实现接收音频数据,数据范围为 -32767 到 32768,0为静音。
ESP32的DAC精度输出为8位,范围0~255,在127或128时应该为静音。由于麦克风录制精度为 12 位,播放为 8 位精度,因此内部存储数据也适合 8 位。 在输出到外部时,可以存储 16 位,但即使存储为8位,音质也几乎保持不变,因为麦克风的收音性能不是那么高,播放声音使用Spk HAT
[mw_shl_code=arduino,true]#include <M5StickC.h>
#include <driver/i2s.h>
#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
((x)<< 8 & 0x00FF0000UL) | \
((x)>> 8 & 0x0000FF00UL) | \
((x)>>24 & 0x000000FFUL) )
#define PIN_CLK (0) // I2S Clock PIN
#define PIN_DATA (34) // I2S Data PIN
#define SAMPLING_RATE (16384) // 采样率(44100, 22050, 16384, more...)
#define BUFFER_LEN (1024) // 缓冲长度
#define STORAGE_LEN (102400) // 本体保存容量(MAX 100K前後)
#define WAVE_EXPORT (0) // WAVE文件标志位
uint8_t soundBuffer[BUFFER_LEN]; // DMA发送
uint8_t soundStorage[STORAGE_LEN]; // 存储声音数据
bool recFlag = false; // 录音状态
int recPos = 0; // 录音长度
void i2sRecord() {
// 录音设定
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
.sample_rate = SAMPLING_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ALL_RIGHT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = BUFFER_LEN,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0,
};
// PIN设定
i2s_pin_config_t pin_config;
pin_config.bck_io_num = I2S_PIN_NO_CHANGE;
pin_config.ws_io_num = PIN_CLK;
pin_config.data_out_num = I2S_PIN_NO_CHANGE;
pin_config.data_in_num = PIN_DATA;
// 配置I2S设置
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
i2s_set_clk(I2S_NUM_0, SAMPLING_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
// 录音开始
recFlag = true;
xTaskCreatePinnedToCore(i2sRecordTask, "i2sRecordTask", 2048, NULL, 1, NULL, 1);
}
// 声音播放
void i2sPlay(){
// 播放设定
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
.sample_rate = SAMPLING_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = BUFFER_LEN,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0,
};
// 配置I2S
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, NULL);
i2s_zero_dma_buffer(I2S_NUM_0);
// 重放
size_t transBytes;
size_t playPos = 0;
while( playPos < recPos ){
for( int i = 0 ; i < BUFFER_LEN ; i+=2 ){
soundBuffer = 0; // 忽略后8位
soundBuffer[i+1] = soundStorage[playPos]; // 输入前8位unit8数据
playPos++;
}
// 传送数据
i2s_write(I2S_NUM_0, (char*)soundBuffer, BUFFER_LEN, &transBytes, (100 / portTICK_RATE_MS));
Serial.println(playPos);
}
// 完成后卸载I2S
i2s_zero_dma_buffer(I2S_NUM_0);
i2s_driver_uninstall(I2S_NUM_0);
}
// 录音任务
void i2sRecordTask(void* arg)
{
// 初始化
recPos = 0;
memset(soundStorage, 0, sizeof(soundStorage));
// 录音处理
while (recFlag) {
size_t transBytes;
// 从I2S获取数据
i2s_read(I2S_NUM_0, (char*)soundBuffer, BUFFER_LEN, &transBytes, (100 / portTICK_RATE_MS));
// int16_t(12bit精度)转uint8_t
for (int i = 0 ; i < transBytes ; i += 2 ) {
if ( recPos < STORAGE_LEN ) {
int16_t* val = (int16_t*)&soundBuffer;
soundStorage[recPos] = ( *val + 32768 ) / 256;
recPos++;
}
}
Serial.printf("transBytes = %d, STORAGE_LEN=%d, recPos=%d\n", transBytes, STORAGE_LEN, recPos);
vTaskDelay(1 / portTICK_RATE_MS);
}
i2s_driver_uninstall(I2S_NUM_0);
// 删除任务
vTaskDelete(NULL);
}
void setup() {
M5.begin();
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(WHITE);
M5.Lcd.setTextColor(BLACK, WHITE);
M5.Lcd.println("Sound Recorder");
M5.Lcd.println("BtnA Record");
M5.Lcd.println("BtnB Play");
}
void loop() {
M5.update();
if ( M5.BtnA.wasPressed() ) {
// 录音开始
M5.Lcd.setCursor(0, 24);
M5.Lcd.println("REC...");
Serial.println("Record Start");
i2sRecord();
} else if ( M5.BtnA.wasReleased() ) {
// 停止录音
M5.Lcd.setCursor(0, 24);
M5.Lcd.println(" ");
recFlag = false;
delay(100); // 等待录音结束
Serial.println("Record Stop");
// 串口打印WAVE文件
if ( WAVE_EXPORT ) {
Serial.printf("52494646"); // RIFF文件头
Serial.printf("%08lx", htonl(recPos + 44 - 8)); // 总数据大小+44(信息块大小)-8(标题大小)
Serial.printf("57415645"); // WAVE头
Serial.printf("666D7420"); // 格式信息块
Serial.printf("10000000"); // 格式大小
Serial.printf("0100"); // 格式代码
Serial.printf("0100"); // 通道数
Serial.printf("%08lx", htonl(SAMPLING_RATE)); // 采样率
Serial.printf("%08lx", htonl(SAMPLING_RATE)); // 字节/秒
Serial.printf("0200"); // 块边界
Serial.printf("0800"); // 真实音频数据
Serial.printf("64617461"); // 数据块
Serial.printf("%08lx", htonl(recPos)); // 总数据大小
for (int n = 0; n <= recPos; n++) {
Serial.printf("%02x", soundStorage[n]);
}
Serial.printf("\n");
}
} else if ( M5.BtnB.wasReleased() ) {
// 开始播放
M5.Lcd.setCursor(0, 24);
M5.Lcd.println("Play...");
Serial.println("Play Start");
i2sPlay();
M5.Lcd.println(" ");
Serial.println("Play Stop");
}
delay(10);
}[/mw_shl_code]
|
|