PROJECT/Speaker

Speaker 만들기 2편 (SD카드에 있는 wav파일 재생)

원원 2025. 10. 12. 20:24

안녕하세요. 오늘은 SD카드에있는 wav 파일을 재생시켜보겠습니다.
1편에서와 같이 ESP32와 MAX98357A를 이용합니다.


*선 연결
SD카드
1. CS   → GPIO5   (SD_CS)
2. MOSI → GPIO23  (DI)
3. MISO → GPIO19  (DO)
4. SCK  → GPIO18  (CLK)

MAX98357A
1. BCLK → GPIO26   (I2S Bit Clock)
2. LRC  → GPIO25   (I2S Left/Right Clock, WS)
3. DIN  → GPIO22   (I2S Data Out, SD)


* 코드의 흐름
1. SD카드 초기화
2. SD카드에있는 특정 wav파일 정보 읽기
3. wav파일 정보를가지고 I2S 초기화
4. wav 파일 재생
5. wav 파일 재생이 끝나면 0.5초후에 다시 재생

#include <ESP_I2S.h>
#include <SD.h>
#include <SPI.h>

#define BCLK 26
#define LRC  25
#define DIN  22
#define SD_CS 5

I2SClass i2s;
File f;

void setup() {
  Serial.begin(115200);

  // SD CARD INIT
  SPI.begin(18, 19, 23, SD_CS);
  if (!SD.begin(SD_CS, SPI)) {
    Serial.println("SD init failed!");
    while (1);
  }
  Serial.println("SD init OK");

  f = SD.open("/test.wav");
  if (!f) {
    Serial.println("WAV open failed!");
    while (1);
  }

  // WAV HEADER READ
  uint8_t header[44];
  f.read(header, 44);
  uint16_t audioFormat = header[20] | (header[21] << 8);
  uint16_t numChannels = header[22] | (header[23] << 8);
  uint32_t sampleRate  = header[24] | (header[25] << 8) | (header[26] << 16) | (header[27] << 24);
  uint16_t bitsPerSample = header[34] | (header[35] << 8);

  Serial.println("===== WAV Info =====");
  Serial.printf("Audio Format : %u (%s)\n", audioFormat, (audioFormat == 1) ? "PCM" : "Compressed");
  Serial.printf("Channels     : %u (%s)\n", numChannels, (numChannels == 1) ? "Mono" : "Stereo");
  Serial.printf("Sample Rate  : %u Hz\n", sampleRate);
  Serial.printf("Bit Depth    : %u-bit\n", bitsPerSample);
  Serial.println("====================");

  // I2S INIT
  i2s_data_bit_width_t bitWidth;
  switch (bitsPerSample)
  {
    case 16: bitWidth = I2S_DATA_BIT_WIDTH_16BIT; break;
    case 24: bitWidth = I2S_DATA_BIT_WIDTH_24BIT; break;
    case 32: bitWidth = I2S_DATA_BIT_WIDTH_32BIT; break;
    default:
      Serial.printf("Unsupported bit depth: %u\n", bitsPerSample);
      while (1);
  }

  i2s.setPins(BCLK, LRC, DIN);
  if (!i2s.begin(I2S_MODE_STD, sampleRate, bitWidth,
                 (numChannels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO)) {
    Serial.println("I2S init failed!");
    while (1);
  }


}

void loop() {
  static uint8_t buf[4096];

  // WAV FILE READ
  if (f.available()) {
    int len = f.read(buf, sizeof(buf));
    if (len > 0) {
      i2s.write(buf, len);
    }
  } else {
    Serial.println("Play end → restart");
    f.seek(44);
    delay(500);
  }
}

f.seek(44); 를 하는 이유는 wav파일에서 오디오데이터 시작위치로 파일 포인터를 이동시키기위해  사용했습니다.

wav파일은 1khz 사인파 음원을 사용했습니다.

 

참고자료
max98357 동작관련 : https://wowon.tistory.com/366
wav파일관련 : https://wowon.tistory.com/369
이 글에서 사용한 wav파일 만들기 : https://wowon.tistory.com/368