{
    "componentChunkName": "component---src-templates-blog-post-jsx",
    "path": "/post/how-to-convert-pcm-to-wav-with-ffmpeg/",
    "result": {"data":{"site":{"siteMetadata":{"title":"WEB EGG","author":"Leko - CTO at Yuimedi"}},"markdownRemark":{"id":"de161836-8297-5341-9f83-94f59bb27ceb","excerpt":"こんにちは。 ffmpegでPCM…","html":"<p>こんにちは。<br>\nffmpegでPCM音源を変換するときに、期待したとおりに変換されなくてハマったので備忘録を残します。</p>\n<p>渡しの場合、音声を扱うプログラムも経験がなく、そもそもサンプリングレートとかの用語からして分からないという感じでした。<br>\n同じ悩みを抱える方がいればと思い備忘録を残します。</p>\n<!--more-->\n<h2 id=\"用語の説明\" style=\"position:relative;\"><a href=\"#%E7%94%A8%E8%AA%9E%E3%81%AE%E8%AA%AC%E6%98%8E\" aria-label=\"用語の説明 permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>用語の説明</h2>\n<p>音声ファイルについての用語を調べてまとめて書こうと思ったのですが、<br>\nこの記事とWikipedia読めばだいたい必要な情報は揃ってしまったので、ご紹介にとどめます。</p>\n<blockquote>\n<p>— <a href=\"http://aviutl.info/sannpurinngure-to-bittosinngo/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">サンプリングレート・ビット深度・ビットレートの意味と関係性</a></p>\n</blockquote>\n<h2 id=\"pcmってなに\" style=\"position:relative;\"><a href=\"#pcm%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%AB\" aria-label=\"pcmってなに permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>PCMってなに</h2>\n<p>PCMは音声波形を電子化したもので、「音」そのものと捉えるとしっくり来ました。</p>\n<blockquote>\n<p>— <a href=\"https://kotobank.jp/word/PCM-7659\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">PCM(ぴーしーえむ)とは - コトバンク</a></p>\n</blockquote>\n<blockquote>\n<p>— <a href=\"http://www.hikari-ongaku.com/study/pcm.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">ＰＣＭの基礎知識</a></p>\n</blockquote>\n<h2 id=\"pcmとwaveファイルの違い\" style=\"position:relative;\"><a href=\"#pcm%E3%81%A8wave%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E9%81%95%E3%81%84\" aria-label=\"pcmとwaveファイルの違い permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>PCMとWAVEファイルの違い</h2>\n<p>WAVE（.wav）ファイルはよく見る形式だと思うのでファイル自体の説明は割愛します。ここでは違いについてだけ。<br>\n<strong>.wavとPCMの大きな違いは、メタデータの有無</strong>です。</p>\n<p>PCMはただの音声波形にすぎず、「サンプリングレート」や「ビットレート」などの情報がファイル自体に含まれていません。<br>\n一方WAVEファイルの中にはそれらのメタデータ＋PCMが含まれています。<br>\nこの差が何を起こすか、コードベースで説明します。</p>\n<h2 id=\"問題のコード\" style=\"position:relative;\"><a href=\"#%E5%95%8F%E9%A1%8C%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89\" aria-label=\"問題のコード permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>問題のコード</h2>\n<p>ffmpegをナマで扱うのはつらいので、<a href=\"https://github.com/fluent-ffmpeg/node-fluent-ffmpeg\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">fluent-ffmpeg</a>というnpmパッケージを利用させていただきました。<br>\nバイナリのパスの指定なども柔軟にできるので、Lambdaとかクセのあるランタイム下でも扱いやすいです。</p>\n<p>で、以下が問題のあるコードです。<br>\n変換元の音声ファイルは<code>16000Hz, Signed, 16bit, LPCM</code>です。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> ffmpeg <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fluent-ffmpeg'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pcmPath <span class=\"token operator\">=</span> <span class=\"token string\">'./demo'</span> # pcm\n<span class=\"token keyword\">const</span> destPath <span class=\"token operator\">=</span> <span class=\"token string\">'./demo.wav'</span>\n\n<span class=\"token function\">ffmpeg</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">input</span><span class=\"token punctuation\">(</span>pcmPath<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">inputFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'s16be'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">output</span><span class=\"token punctuation\">(</span>destPath<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>ffmpegは拡張子を見てinput, outputをよしなにしてくれるらしいと噂を聞いたので、シンプルに実装してみました。<br>\n<code>s16be</code>というのは、Signedで16bitでBig endianなPCMのフォーマットを指します。<br>\n変換した結果が、これです。</p>\n<audio src=\"/6aa7d74a96c0687976698a597d54d279/how-to-convert-pcm-to-wav-with-ffmpeg-failed-too-fast.wav\" preload=\"auto\" controls>\n<p>…<strong>なんかキュルキュル言ってる。</strong><br>\n本来であれば、以下ような音声が再生されるのが期待する変換処理です。</p>\n<audio src=\"/ff724ff55993b019d45256832f068dff/how-to-convert-pcm-to-wav-with-ffmpeg-correct.wav\" preload=\"auto\" controls>\n<h2 id=\"pcmにはメタデータがない\" style=\"position:relative;\"><a href=\"#pcm%E3%81%AB%E3%81%AF%E3%83%A1%E3%82%BF%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C%E3%81%AA%E3%81%84\" aria-label=\"pcmにはメタデータがない permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>PCMにはメタデータがない</h2>\n<p>ここでWAVEファイルのバイナリ構成を整理してみます。</p>\n<table>\n<thead>\n<tr>\n<th>開始byte</th>\n<th>終了byte</th>\n<th>byte</th>\n<th>データ内容</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>4</td>\n<td>4</td>\n<td>‘RIFF’の４文字</td>\n</tr>\n<tr>\n<td>5</td>\n<td>8</td>\n<td>4</td>\n<td>総ファイルサイズ-8(byte)</td>\n</tr>\n<tr>\n<td>9</td>\n<td>12</td>\n<td>4</td>\n<td>‘WAVE’の４文字</td>\n</tr>\n<tr>\n<td>13</td>\n<td>16</td>\n<td>4</td>\n<td>‘fmt ‘の４文字　フォーマットチャンク</td>\n</tr>\n<tr>\n<td>17</td>\n<td>20</td>\n<td>4</td>\n<td>フォーマットサイズ　デフォルト値16</td>\n</tr>\n<tr>\n<td>21</td>\n<td>22</td>\n<td>2</td>\n<td>フォーマットコード　非圧縮のPCMフォーマットは1</td>\n</tr>\n<tr>\n<td>23</td>\n<td>24</td>\n<td>2</td>\n<td>チャンネル数　モノラルは1、ステレオは2</td>\n</tr>\n<tr>\n<td>25</td>\n<td>28</td>\n<td>4</td>\n<td>サンプリングレート　44.1kHzの場合なら44100</td>\n</tr>\n<tr>\n<td>29</td>\n<td>32</td>\n<td>4</td>\n<td>バイト／秒　１秒間の録音に必要なバイト数</td>\n</tr>\n<tr>\n<td>33</td>\n<td>34</td>\n<td>2</td>\n<td>ブロック境界　ステレオ16bitなら、16bit*2 = 32bit = 4byte</td>\n</tr>\n<tr>\n<td>35</td>\n<td>36</td>\n<td>2</td>\n<td>ビット／サンプル　１サンプルに必要なビット数</td>\n</tr>\n<tr>\n<td>37</td>\n<td>40</td>\n<td>4</td>\n<td>‘data’の４文字　フォーマットチャンク</td>\n</tr>\n<tr>\n<td>41</td>\n<td>44</td>\n<td>4</td>\n<td>総ファイルサイズ-126</td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>— <a href=\"http://www.graffiti.jp/pc/p030506a.htm\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">WAVEファイルの構造</a></p>\n</blockquote>\n<p>見ての通り、ファイルの中に「チャンネル数」「サンプリングレート」などの情報が保持されています。<br>\nなのでffmpegなどにWAVEファイルのパスを与えれば、ファイルの中身から音声の情報を読み取り変換処理が実行できます。</p>\n<p>一方、<strong>PCMにはこれらの情報が含まれていないので、ファイルパスと音声ファイルのフォーマット（<code>s16be</code>）だけを与えても情報が足りません</strong><br>\nなのでPCMだけをffmpegに与えても、音声プレイヤーやコンバータなどは与えられた音声波形をどう扱えば良いかが分からず、期待した通りに再生/変換されないなどの現象が起こります。</p>\n<h2 id=\"解決策inputoptions\" style=\"position:relative;\"><a href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%96inputoptions\" aria-label=\"解決策inputoptions permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>解決策：inputOptions</h2>\n<p>上記の問題に気づけずに、ひたすらオプションを組み替えて試していたら、偶然うまくいった例がありました。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> ffmpeg <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fluent-ffmpeg'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pcmPath <span class=\"token operator\">=</span> <span class=\"token string\">'./demo.pcm'</span>\n<span class=\"token keyword\">const</span> destPath <span class=\"token operator\">=</span> <span class=\"token string\">'./demo.wav'</span>\n\n<span class=\"token function\">ffmpeg</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">input</span><span class=\"token punctuation\">(</span>pcmPath<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">inputOptions</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'-ac 1'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'-ar 16000'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">inputFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'s16be'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">output</span><span class=\"token punctuation\">(</span>destPath<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p><code>inputOptions</code>が増えました。それ以外は同じです。<br>\n変換するための情報としてサンプリングレート（<code>-ar</code>）が欠けていたようです。</p>\n<p>ということで、PCM音源をffmpegで変換する際には、符号化方式やサンプリングレートなどの<strong>PCMファイルには含まれない情報を明示的に指定する</strong>必要があります。</p>\n<h2 id=\"さいごに\" style=\"position:relative;\"><a href=\"#%E3%81%95%E3%81%84%E3%81%94%E3%81%AB\" aria-label=\"さいごに permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>さいごに</h2>\n<p>変換処理のデモに使わさせていただいた音声は、docomoの音声合成APIを利用しています。<br>\n後日公開ですが、こちらの記事も見ていただけると幸いです。</p>\n<p>（2017/08/08ごろ公開予定）docomoの音声合成APIを利用して無料でVOICEROIDっぽい声を生成してみる</p>","timeToRead":6,"frontmatter":{"title":"ffmpegでPCM音源をWAVE形式に変換するときにハマったこと","tags":["ffmpeg","Nodejs"],"date":"August 03, 2017","featuredImage":{"childImageSharp":{"fluid":{"tracedSVG":"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='400'%20height='98'%20viewBox='0%200%20400%2098'%20preserveAspectRatio='none'%3e%3cpath%20d='M3%203c-5%205-1%2010%209%2010l7%201-9%2010-9%208v17c0%2015%200%2017%202%2018%204%204%205%203%2035-26l29-28h9L39%2051C-1%2091-2%2091%203%2095c1%202%204%202%2019%202h16l24-23%2023-23v9L71%2075C57%2089%2056%2091%2059%2095c2%203%2035%203%2037%200%205-6%201-10-10-10h-7l9-10%2010-10V49c0-18-1-20-6-20-3%200-7%203-31%2028L33%2085H23l37-38C85%2023%2098%209%2098%207l-2-4c-1-2-2-2-18-2H60L38%2024%2014%2047l-1-5v-5l14-13C42%209%2043%206%2039%203%2037%200%205%200%203%203m109%2049v31h15V71l1-12%2011-1h12V47l-12-1h-12v-7l1-6h29V20h-45v32m52%200v31h16V58h23V46h-23V33h29V20h-45v32m208-16c-8%203-12%2010-13%2021-1%2021%2015%2034%2030%2023l3-2%201%203c0%202%201%202%203%202h3V58l-10-1h-10v9h5c4%200%204%200%203%202-1%206-9%208-13%204-6-7-4-26%202-28%205-1%208%200%2010%204s2%204%207%203c6%200%206-2%204-7-5-9-16-12-25-8M215%2059v24h10V47l5%2014%205%2018c1%204%201%204%205%204h4l5-15c8-25%207-25%207-4v19h11V35h-17l-4%2013-5%2014-4-14-4-13h-18v24m62%200v24h11V65h8c6%200%208%200%2011-2%207-5%209-15%205-21s-8-7-22-7h-13v24m43%200v24h35v-9l-12-1h-11V63h18v-9h-18V44h23v-9h-35v24m-32-9v6h5c8%200%2011-3%209-8-1-3-3-4-9-4h-5v6'%20fill='%23d3d3d3'%20fill-rule='evenodd'/%3e%3c/svg%3e","aspectRatio":4.073170731707317,"src":"/static/c54e3bc423bc8b60c0291ef14871ee4b/e4d72/featured-image.png","srcSet":"/static/c54e3bc423bc8b60c0291ef14871ee4b/1ec58/featured-image.png 334w,\n/static/c54e3bc423bc8b60c0291ef14871ee4b/ccb4a/featured-image.png 668w,\n/static/c54e3bc423bc8b60c0291ef14871ee4b/e4d72/featured-image.png 1280w","srcWebp":"/static/c54e3bc423bc8b60c0291ef14871ee4b/135cd/featured-image.webp","srcSetWebp":"/static/c54e3bc423bc8b60c0291ef14871ee4b/cd98f/featured-image.webp 334w,\n/static/c54e3bc423bc8b60c0291ef14871ee4b/7535d/featured-image.webp 668w,\n/static/c54e3bc423bc8b60c0291ef14871ee4b/135cd/featured-image.webp 1280w","sizes":"(max-width: 1280px) 100vw, 1280px"}}}}}},"pageContext":{"slug":"/how-to-convert-pcm-to-wav-with-ffmpeg/","previous":{"fields":{"slug":"/special-chars-for-cli/"},"frontmatter":{"title":"CLIアプリに使えそうな特殊文字たちで遊んでみた","tags":["Nodejs","JavaScript","CLI"]}},"next":{"fields":{"slug":"/voiceloid-like-text2speech/"},"frontmatter":{"title":"docomoの音声合成APIを利用して無料でVOICEROIDっぽい声を生成してみる","tags":["Nodejs","音声合成","ffmpeg"]}}}},
    "staticQueryHashes": ["2585454260","2954598359"]}