M5StickC音频录制与播放-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2153|回复: 0

M5StickC音频录制与播放

[复制链接]
发表于 2020-5-19 15:58 | 显示全部楼层 |阅读模式
本帖最后由 vany5921 于 2020-5-19 15:59 编辑

M5StickC内部自带了一个PDM制式麦克风数据精度12位,数据结构为16位,可以通过配置I2S实现接收音频数据,数据范围为 -32767 到 32768,0为静音。 mic.png

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]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 02:51 , Processed in 0.213060 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表