우선 WAV 파일을 라즈베리파이 피코에서 재생하려면 외부 모듈을 써서 디코딩이 가능한 하드웨어 구성을 해야 합니다.
보통 I2S 연결로 해서 I2S 방식으로 오디오 데이터를 재생합니다. 모듈마다 성능이 다른데, 제가 쓰는 Waveshare Pico-Audio 모듈은 오리지날 리비전으로 된 모듈이구요. 텍사스 인스트루먼트사의 PCM5101A를 DAC로 합니다.
즉 라즈베리파이 피코 보드 1대, 오디오 모듈 1대, 점퍼케이블만 있으면 일단 설비를 실험해볼 수 있네요.
오디오 파일을 저장해서 불러오는 방법이 중요한데요. 피코 내장 플래시는 2MB밖에 안될뿐아니라 BOOTSEL로 uf2를 심을때 데이터가 지워지므로 외부 보조기억장치가 필요합니다. microSD 카드로 해도 되고, USB 메모리도 해도 됩니다.
전 같은 업체의 익스텐더 보드를 써서 보조전원을 해결해서, 라즈베리파이 피코의 USB 단자에 USB 메모리를 장착하는 방법으로 했네요. microSD 카드는 모듈 5개로 번갈아가면서 시도했고, 소스코드 메트릭을 조정해도 안되기에 USB 메모리 체제로 바꾸니 됩니다.
설비는 대충 이렇구요. 코드를 만들때 고려할 사안들은 아래와 같습니다.
(1) USB MSC를 Host로 돌리는 구현
(2) FatFS으로 장치를 마운트후, 이진파일 형태(?)로 음악파일 읽어오기
(3) 링버퍼에 음악파일 데이터를 저장. 오프셋만큼 조금씩 읽어들이기
(4) I2S 초기화
(5) 쓰기동작으로 오디오 재생
이런게 핵심이구요.
(1) USB MSC를 Host로 돌리는 구현은 TinyUSB를 활용하면 쉽습니다. TinyUSB는 라즈베리파이 재단 외적인 프로젝트인데요. USB 작동 모드별로 구현된 기능이 강력하고, 각 클래스별로 활용도가 큰 기능을 이미 구현된 코드로 제공합니다. 라즈베리파이 피코에서도 C SDK에서 제공을 하는데요. 콜백만 잘 구현하면, main() 함수에서 아주 간단한 몇개의 함수만 호출하면 USB 관련 구현이 되는 원리입니다.
(2) FatFS로 장치 마운트후 이진파일 형태로 오디오 파일을 읽어들이는 매커니즘은 FatFS가 워낙에 강력한 파일 입출력 프로젝트라 거의 표준인데요. f_mount()로 (1)에 기반한 보조기억장치를 마운트하고, f_open()으로 파일을 생성하거나 열어서 f_read()로 읽어들입니다. f_read()가 중요한데요. 오프셋만큼 eof에 도달할때까지 루프를 돌려서 조금씩 읽어들여 링버퍼에 집어넣고 이를 I2S에 보내서 재생합니다. 오프셋 설정과 이를 반영하는 코드가 중요하네요.
(3) 링버퍼는 큐로 구현된다는 것 같구요. 우선 WAV 파일 포맷의 헤더를 읽어서 정보를 활용하면 좋구요. FMT 청크 같은 부분을 읽어들여서 처리하고 헤더만큼 오프셋을 옮겨서 오디오 데이터가 있는 부분을 링버퍼에서 감지해서 I2S에 보내는 원리 같애요. 쓰기동작으로 하면 된다고 압니다.
(4) I2S 초기화는 피코 C SDK의 pico-extras에 있는 audio_i2s 프로젝트를 어떤 분이 튜닝하기도 했는데요. 피코에서 PIO를 정의하는 pioasm을 작성해서 동작을 시킵니다. 작성자마다 조금씩 다른 구문인데 일단 기존의 프로젝트를 가져와서 하면 편합니다. 단, 프로젝트마다 제공하는 I2S 초기화 함수가 다르므로 잘 보고 음질과 코드를 타협하면 됩니다. 전 일단 I2S에 보내기만 할 것이고 16비트 44.1KHz만 되면 되기에 pico-extras에서 제공하는 정도면 되는데, 참고한 프로젝트의 코드를 실험하느라, 바꾸어진 코드로 대체했습니다.
프로듀서풀과 컨슈머풀이 버퍼 관리에 쓰이기도 하는데요. 이를 활용하면 좋은데, 일단 기재해둡니다.
처리가 되서 링버퍼에 적절하게 데이터가 대입되면 I2S에 쓰기동작해서 재생하면 되는 것 같습니다.
두서없는데, 일단 이해한 것을 정리해봅니다.
I2S를 I2C로 오타내서 고쳐두었습니다.