Выборка I2S-данных и сохранение на SD-карту как wav воспроизводит исходный звук?

Меня беспокоит сохранение аудиоданных с 2-х I2S MEMS-микрофонов на SD-карту с ESP32 WROOM, и, похоже, мне это не повезло ...

Моя идея: путем опроса флага прерывания (активируется всякий раз, когда буфер DMA заполнен), предлагаемого espressif I2S-API, я вызываю вызов I2S_read и сохраняю полученные данные в больший буфер. Со временем этот буфер заполняется и отправляется на SD-карту, где wav-заголовок уже ожидает данные. Информация о длине в заголовке обрабатывается последней задачей записи, которая получает свою информацию путем вычитания функции millis() времени ОС при нажатии кнопки записи и другого вызова millis() при нажатии кнопки опять таки.

мои ресурсы:

  • ESP32 WROOM
  • 2x МЭМС-микрофона ADAFRUIT SPH0645 (стерео I2S)
  • Адаптер ADAFRUIT для карты micro-SD (SPI)
  • кнопка внешнего прерывания
  • платформаIO
  • freeRTOS
  • частота дискретизации 44100 Гц
  • битовая глубина 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, 1, 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-заголовок-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);
}

спасибо за любую помощь, очень ценим.~ Джейк

0 ответов

Другие вопросы по тегам