【CircuitPython】ValueError: fmtチャンクの後にdataチャンクが続かなければなりません

# 0. はじめに

Raspberry Pi Pico で CircuitPython を使って、WAVファイルを再生するとき、『ValueError: fmtチャンクの後にdataチャンクが続かなければなりません』のエラーとなって再生できないWAVファイルが存在する。

このエラーの対応策を調べました。

WAVファイルの再生は、[Adafruitのサイト](https://learn.adafruit.com/mp3-playback-rp2040/pico-i2s-mp3#i2s-wav-file-playback-3101807)で紹介されている、下記コードで確認。
（⭐️の箇所でエラーが起こる）

![play.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/49802f19-959f-bfb3-8a4f-ad9eef071ada.png)

> Adafruit CircuitPython 9.0.4 on 2024-04-16; Raspberry Pi Pico with rp2040



# 1. エラーの原因

一口に [WAVファイル](https://en.wikipedia.org/wiki/WAV)といっても、ファイルの中身（ファイルフォーマット）には、何パターンもあり、その一部のファイルフォーマットに対応していないことが原因のようです。

ネットに公開されているフリー音源や、オンラインのメディア変換（MP3→WAV）で作ったWAVファイルでも、このエラーになるファイルがあります。もちろん、普通に再生できるWAVファイルもあります。

これに関しては深追いはしていません。（コーデックの違い??）


# 2. 対応策

[`ffmpeg`](https://ffmpeg.org)コマンドでファイルを修復します（修復という表現が正しいのか分かりません）。

ffmpegコマンドに次のオプションを指定します。

```
ffmpeg -i "original.wav" -bitexact -acodec pcm_s16le -ac 2 -ar 16000 -map_metadata -1 "repaired.wav"

or

ffmpeg -i "original.mp3" -bitexact -acodec pcm_s16le -ac 2 -ar 16000 -map_metadata -1 "converted.wav"
```

モノラル音源の場合は、`-ac 2`を`-ac 1`にする。

出典；

https://github.com/adafruit/Adafruit_CircuitPython_CircuitPlayground/issues/97


## 2.1 修復前後のファイルの違い

```terminal:⓪大元の音源ファイル
$ ffmpeg -hide_banner -i hapyou2.mp3
[mp3 @ 0x11ce06400] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from 'hapyou2.mp3':
  Duration: 00:00:05.12, start: 0.000000, bitrate: 128 kb/s
  Stream #0:0: Audio: mp3 (mp3float), 44100 Hz, stereo, fltp, 128 kb/s
At least one output file must be specified
```

```terminal:①オンラインオンバータで変換したWAVファイル（再生できない）
$ ffmpeg -hide_banner -i hapyou2.wav
[aist#0:0/pcm_s16le @ 0x122606120] Guessed Channel Layout: stereo
Input #0, wav, from 'hapyou2.wav':
  Metadata:
    encoder         : Lavf59.27.100
  Duration: 00:00:05.12, bitrate: 1411 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
At least one output file must be specified
```

```terminal:②ffmpegで修復したWAVファイル（再生できる）
$ ffmpeg -hide_banner -i hapyou2.wav -bitexact -acodec pcm_s16le -ac 2 -ar 16000 -map_metadata -1 repaired.wav
[aist#0:0/pcm_s16le @ 0x12b6069d0] Guessed Channel Layout: stereo
Input #0, wav, from 'hapyou2.wav':
  Metadata:
    encoder         : Lavf59.27.100
  Duration: 00:00:05.12, bitrate: 1411 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'repaired.wav':
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, stereo, s16, 512 kb/s
      Metadata:
        encoder         : Lavc pcm_s16le
[out#0/wav @ 0x600003120540] video:0KiB audio:320KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.013428%
size=     320KiB time=00:00:05.12 bitrate= 512.1kbits/s speed= 826x    

$ ffmpeg -hide_banner -i repaired.wav
[aist#0:0/pcm_s16le @ 0x120e072c0] Guessed Channel Layout: stereo
Input #0, wav, from 'repaired.wav':
  Duration: 00:00:05.12, bitrate: 512 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, stereo, s16, 512 kb/s
At least one output file must be specified
```

```terminal:③ffmpegで変換したWAVファイル（再生できる）
$ ffmpeg -hide_banner -i hapyou2.mp3 -bitexact -acodec pcm_s16le -ac 2 -ar 16000 -map_metadata -1 converted.wav
[mp3 @ 0x131e06d70] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from 'hapyou2.mp3':
  Duration: 00:00:05.12, start: 0.000000, bitrate: 128 kb/s
  Stream #0:0: Audio: mp3 (mp3float), 44100 Hz, stereo, fltp, 128 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (mp3float) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'converted.wav':
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, stereo, s16, 512 kb/s
      Metadata:
        encoder         : Lavc pcm_s16le
[out#0/wav @ 0x6000024f4600] video:0KiB audio:320KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.013428%
size=     320KiB time=00:00:05.12 bitrate= 512.1kbits/s speed= 678x    

$ ffmpeg -hide_banner -i converted.wav
[aist#0:0/pcm_s16le @ 0x13a607340] Guessed Channel Layout: stereo
Input #0, wav, from 'converted.wav':
  Duration: 00:00:05.12, bitrate: 512 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, stereo, s16, 512 kb/s
At least one output file must be specified
```

①の再生できないWAVファイルと、②③の再生できるWAVファイルのffmpeg情報から、サンプリング周波数（とビットレート）の違いが見て取れる。
では、サンプリング周波数44.1KHzのWAVファイルがエラーとなるのか確認したが、問題なく再生できた。サンプリング周波数がエラーの原因では無い。

```terminal:④ffmpegで44.1KHzのWAVファイルに変換（再生できた）
$ ffmpeg -hide_banner -i hapyou2.mp3 -bitexact -acodec pcm_s16le -ac 2 -ar 44100 -map_metadata -1 converted441.wav
[mp3 @ 0x12cf054f0] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from 'hapyou2.mp3':
  Duration: 00:00:05.12, start: 0.000000, bitrate: 128 kb/s
  Stream #0:0: Audio: mp3 (mp3float), 44100 Hz, stereo, fltp, 128 kb/s
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (mp3float) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'converted441.wav':
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
      Metadata:
        encoder         : Lavc pcm_s16le
[out#0/wav @ 0x600001d4c000] video:0KiB audio:882KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.004872%
size=     882KiB time=00:00:05.12 bitrate=1411.3kbits/s speed= 584x    

$ ffmpeg -hide_banner -i converted441.wav
[aist#0:0/pcm_s16le @ 0x147e072c0] Guessed Channel Layout: stereo
Input #0, wav, from 'converted441.wav':
  Duration: 00:00:05.12, bitrate: 1411 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
At least one output file must be specified
```

```terminal
$ ffmpeg -version
ffmpeg version 7.0 Copyright (c) 2000-2024 the FFmpeg developers
built with Apple clang version 15.0.0 (clang-1500.3.9.4)
```

②③の再生できるWAVファイルの中身は同じかと想定したが、コンペアしてみたらそうではなかった。ファイルサイズは同じ。


```terminal:
$ cmp converted.wav repaired.wav
converted.wav repaired.wav differ: char 937, line 1

$ ls -ls converted.wav repaired.wav
648 -rw-r--r--  1 user_name  staff  327724  5 22 20:29 converted.wav
648 -rw-r--r--  1 user_name  staff  327724  5 22 20:38 repaired.wav
```


# MicroPythonではエラーとならない

ちなみに、MicroPythonを使い、下記サイトのコードで再生すると、CircuitPythonではエラーとなるWAVファイルでも、エラーにならず再生できました。
CircuitPython（Adafruit）の実装の違いでしょうか。

- [How to get i2s playback with Raspberry Pi Pico](https://orionrobots.co.uk/2023/09/04/i2s-playback.html)

https://orionrobots.co.uk/2023/09/04/i2s-playback.html

> MicroPython v1.22.2 on 2024-02-22; Raspberry Pi Pico W with RP2040

# 追伸

Raspberry Pi Pico のフラッシュメモリは2MBしか無いため、CircuitPython / MicroPythonのファームウェアを入れると、空き容量はわずかとなり、数秒のWAVファイルしか再生できない。
10秒で2MBぐらいになるため、大きいWAVファイルを再生するためには、外付けのSDカードから再生するしかない。
これについては、別の記事とする予定。


#### 以上