次のネタのため、ここらでwavファイルの読み書きについてまとめておきます。
WAVファイルの構造
全体の構造
WAVファイルは、RIFFとよばれる型式のファイルの一種です。RIFFとはResource Interchange File Formatの略で、画像や音声を格納する汎用のファイル形式です。
RIFFは、チャンク(=chunk、もとは『大きな塊』を意味する英単語)と呼ばれるデータの単位が並んだ構造をしています。ファイル全体としては以下のようなイメージです。
チャンクには、RIFFチャンク、fmtチャンク、LINKチャンク、JUNKチャンク、dataチャンクなどいろいろな種類があります(上記以外にもあります)。このうちRIFFチャンクがデータ全体を表し、その中にRIFFチャンクのデータとして他のチャンクが一列に並ぶ構造になっています。このように他のチャンクのデータとなっているチャンクのことをサブチャンクと言います。サブチャンクの順番は不定です。
チャンクの基本構造(全チャンク共通)
各チャンクは、基本的に以下のようなフィールドを持っています。
※『位置』はチャンクの先頭を0としてバイト単位で表す(以下全てのチャンクの説明も同じ)
位置 | バイト数 | フィールド名 | 意味、値の例 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | チャンクの種類を表す文字列 |
4~7 | 4 | チャンクデータのサイズ | チャンクデータのバイト数 |
8~ | 任意長 | チャンクデータ | チャンクのデータの本体 |
各フィールド
チャンク識別子
各チャンク先頭に4バイトの識別子があります。これはチャンクの名前を表す4文字のASCIIコードです。fmtのように名前が4文字に満たない場合は空白文字(0x20)が補われて4バイトになります。
LISTチャンクなら “LIST”=0x4C 49 53 54
fmt チャンクなら “fmt “= 0x66 6D 74 20 (最後に空白文字があることに注意)
など
チャンクデータのサイズ
チャンクデータのバイト数を表します。チャンク全体のサイズからチャンク識別子とチャンクデータのサイズ、合計8バイトを引いた値です。
※RIFFチャンクやLISTチャンクのように、データ本体の前にデータタイプを表すフィールドがあったとしても、チャンクデータのサイズはチャンク全体のサイズから8を引いた値になっています。
次のチャンク(もし次のチャンクがあるとすれば)の開始位置は、このチャンクの開始位置+8+チャンクデータのサイズとなります。ファイル読み込みを実装する場合は、チャンクデータのサイズを読んだ直後にチャンクデータサイズ分だけファイルポインタを進めれば次のチャンクの先頭に移動できます。
チャンクデータ
チャンクデータの内容はチャンクの種類によって異なります。
データの表現
文字列データ
チャンク識別子などの文字列データは、文字順のASCIIコードで格納されます。
数値データ
数値をマルチバイトの列にすると下位のバイトが先になります。
よって、たとえば数値『1』を2バイトで書き込むと、ファイル上は 0x00 01 ではなく 0x01 00 となります。
各チャンクの構造
では、各チャンクの構造をみていきましょう。
RIFFチャンク
位置 | バイト数 | 項目 | 意味、値の例 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | ”RIFF”=0x52 49 46 46 に固定 |
4~7 | 4 | チャンクデータのサイズ | チャンクデータのバイト数 |
8~11 | 4 | データフォーマット | wavファイルの場合は”WAVE”=0x57 41 56 45 に固定 |
12~ | 任意長 | チャンクデータ | fmt、dataなどのチャンクが並ぶ |
各フィールド
チャンク識別子
RIFFチャンクの識別子は、文字列 “RIFF”=0x52 49 46 46 に固定です。
チャンクデータのサイズ
RIFFチャンクはそのデータとして、後で説明するfmtチャンクやdataチャンクなどすべてのチャンクを含んでいます。このように別のチャンクのデータとして含まれているチャンクをサブチャンクと言います。よって、RIFFチャンクのデータサイズは、全サブチャンクサイズの合計=ファイルサイズ-8バイト(RIFFチャンクの『チャンク識別子』フィールド4バイトと『チャンクデータサイズ』フィールド4バイト)を引いたものです。
※『データフォーマット』フィールドはRIFFチャンクのチャンクデータの一部と考えます
データフォーマット
RIFFに含まれるデータの型式を表します。WAVEファイルの場合は文字列 “WAVE”=0x57 41 56 45です。
チャンクデータ
RIFFチャンクのデータはfmtチャンクやdataチャンクなどのサブチャンクです。最初のサブチャンク(wavファイルの場合はfmtチャンク)の開始位置は、ファイル先頭を0バイト目として12バイト目です。
fmt チャンク
wavファイルでは、fmtチャンクは必須です。ほとんどの場合、RIFFチャンクのサブチャンクの中で最初に配置されます。
非圧縮リニアPCMのwavファイルの場合は最後の拡張パラメータの部分が存在しないことが多いので、fmtチャンクのサイズは24bytesです。とはいえ、任意のwavファイルを読み込むプログラムを作成する場合はちゃんとチャンクデータのサイズを確認するように実装するべきです。
位置 | バイト数 | 項目 | 意味、値 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | “fmt “=0x66 6d 74 20に固定 |
4~7 | 4 | チャンクデータのサイズ | fmtチャンクのデータのサイズ (非圧縮リニアPCMでは基本的に16バイト) |
8~9 | 2 | 音声の型式 | 非圧縮リニアPCMならば 1= 0x01 00 |
10~11 | 2 | チャンネル数 | 音声データのチャンネル数 |
12~15 | 4 | サンプリング周波数 | サンプリング周波数、44.1kHz=44100など |
16~19 | 4 | 1秒あたりのデータのバイト数の平均 | 非圧縮の場合はブロックサイズ×サンプリング周波数 |
20~21 | 2 | ブロックサイズ | 1回のサンプリングで使用するバイト数。 (量子化ビット数÷8)×チャンネル数 |
22~23 | 2 | 量子化ビット数 | 1サンプルあたりのビット数、8や16 |
24~25 | 2 | 拡張パラメータのサイズ | この項目自体がない場合もあります |
26~ | 不定 | 拡張パラメータ | この項目自体がない場合もあります |
各フィールド
チャンク識別子
fmtチャンクでは、文字列”fmt “=0x66 6d 74 20に固定です。4文字目が空白文字であることに注意して下さい。
チャンクデータのサイズ
fmtチャンクのデータのサイズです。非圧縮リニアPCMなら(拡張データがないので)16バイト
…と、参考にした本には書いてあったのですが、実は異なるバイト数のファイルも存在するので読み込みプログラムを作る場合には注意が必要です。
音声の型式
音声データの型式を表しています。
非圧縮リニアPCMならば 1= 0x01 00
G.711 A-lawならば 6=0x06 00
G.711 μ-lawならば 7=0x07 00
などとなります。
チャンネル数
音声データのチャンネル数を表します。
モノラルなら1=0x01 00
ステレオなら2=0x02 00
となります。
サンプリング周波数
サンプリング周波数(1秒間に何回サンプリングするか)を表します。
48kHz(業務用素材)ならば 48000=0x80 BB 00 00
44.1kHz(CD音質)ならば 44100=0x44 AC 00 00
8kHz ならば 8000=0x40 1F 00 00
となります。
1秒あたりのデータのバイト数の平均
『平均』となっているのは、データ本体が圧縮形式の場合に時間あたりのデータ量が一定では無いためでしょう。
非圧縮PCMの場合は、
ブロックサイズ×サンプリング周波数
で求められる値と同じで固定です。
たとえばCD音質の場合、サンプリング周波数44.1kHz、ブロックサイズ(後述)4なので
4×44100=176400=0x10 B1 02 00
となります。
ブロックサイズ
1回サンプリングで使用するバイト数です。
(量子化ビット数÷8)×チャンネル数
で求められる値と同じです。
たとえばCD音質の場合は、量子化ビット数16、ステレオ(チャンネル数2)なので
(16÷8)×2=4=0x04 00
となります。
量子化ビット数
1サンプル(1回のサンプリングの1チャンネル分)のデータを表すビット数です。
音楽用の場合は 16ビット=0x10 00
がよく使用されます。
拡張パラメータのサイズ
拡張パラメータのバイト数です。2バイトですが、データ本体が非圧縮リニアPCM型式の場合はこのフィールド自体が存在しないことが多いです。
拡張パラメータ
内容は様々ですが、 データ本体が非圧縮リニアPCM型式の場合はこのフィールド自体が存在しないことが多いです。
dataチャンク
wavファイルでは、dataチャンクは必須です。ただしfmtチャンクの直後に配置されているとは限りません。市販CDからリッピングした場合、JUNQチャンクなどが先に配置され、dataチャンクはその後になることもあるようです。
位置 | バイト数 | 項目 | 意味、値の例 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | ”data”=0x50 45 41 4B に固定 |
4~7 | 4 | チャンクデータのサイズ | チャンクデータのbyte数 |
8~ | 任意長 | チャンクデータ | 音声データ本体 |
各フィールド
チャンク識別子
dataチャンクでは、文字列”data”=0x50 45 41 4B で固定です。
チャンクデータのサイズ
dataチャンクのデータのbyte数です。
後述の理由により、量子化ビット数8・モノラルでも必ず偶数になります。
チャンクデータ
音声データの本体です。
モノラルの場合は、
のように、サンプリングされたデータが順に並んでいます。
ステレオの場合は、
のように、左チャンネルのサンプリングデータ、右チャンネルのサンプリングデータ…と交互に並んでいます。
全データのバイト数が奇数の場合、最後に1バイト追加して偶数になるように調整されます。
LISTチャンク
位置 | バイト数 | 項目 | 意味、値の例 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | ”LIST”=0x4C 49 53 54 に固定 |
4~7 | 4 | チャンクデータのサイズ | チャンクデータのbyte数 |
8~11 | 4 | リストタイプ | LISTチャンクのタイプをあらわす4文字の文字列 |
12~ | 任意長 | チャンクデータ | 様々なサブチャンク |
各フィールド
チャンク識別子
LISTチャンクでは、文字列”LIST”=0x4C 49 53 54 で固定です。
チャンクデータのサイズ
LISTチャンクのデータのbyte数です。
リストタイプ
LISTチャンクのタイプを表す文字列です。”INFO”=0x49 4E 46 4F などがあります。
チャンクデータ
チャンクデータには、サブチャンクが並びます。
LISTチャンクとサブチャンクの関係は、RIFFチャンクとfmtチャンクやLISTチャンクの関係と同様です。サブチャンクはそれぞれチャンク共通の構造の通り、冒頭4バイトがチャンク識別子、次の4バイトがチャンクデータサイズになっていて、サイズを見ながら順に辿れるようになっています。
LIST_INFOのサブチャンク
ここでは、LISTチャンクのタイプがINFOだった場合について、使用されるサブチャンクをまとめています。非常に種類が多いようなのですが、手元のCDで使われていたのは以下のものでした。
チャンク識別子 | 名称 | 意味 |
---|---|---|
IART | Artist | 演奏者 |
ICMT | Comment | コメント |
ICOP | Copyright | 著作権 |
ICRD | Date Created | 作成された日付 |
IENG | Engineer | エンジニア |
IGNR | Genre | 楽曲のジャンル |
INAM | Title | 楽曲のタイトル |
IPRD | Product | 制作 |
ISFT | Software | ソフトウェア |
TRCK | Truck Number | CDなどメディア内でのトラック番号 |
_PMX/XMPチャンク
_PMXまたはXMPチャンクとは、XMP(=Extensible Metadata Platform)という、マルチメディアファイルにメタデータを埋め込むXML型式の文書を格納しています。
位置 | バイト数 | 項目 | 意味、値の例 |
---|---|---|---|
0~3 | 4 | チャンク識別子 | ”_PMX”=0x5F 50 4D 58 に固定 |
4~7 | 4 | チャンクデータのサイズ | チャンクデータのbyte数 |
8~ | 任意長 | チャンクデータ | XMP(XML)のテキストデータ |
各フィールド
チャンク識別子
_PMXチャンクでは、文字列”_PMX”=0x5F 50 4D 58で固定です。
チャンクのデータサイズ
_PMXチャンクのデータのbyte数です。
チャンクデータ
XMP(XML型式)のテキストデータが格納されています。
XMPデータは、
ルート要素は必ず x:xmpmeta 要素
x:xmpmeta 要素の中に1つだけ rdf:RDF 要素
rdf:RDF要素の中に複数の rdf:Description 要素があり、その中にメタデータを記述する
となっているXML文書です。wavファイルに埋め込む場合は、さらに全体を
<?xpacket begin=”バイトオーダーマーク id=”W5M0MpCehiHzreSzNTczkc9d”?>
~<?xpacket end=”w”>
で囲みます。バイトオーダーマークは UTF-8なら0xEF BB BF、UTF-16なら0xFE FFなどです。
全体では以下のような感じです(架空のデータです)。
各行行頭の空白や改行も含めて格納されています。
<?xpacket begin="バイトオーダーマーク(UTF-8なら0xEF BB BF)" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmp:CreateDate="2021"
xmpDM:genre="クラシック"
xmpDM:album="交響曲第1番"
xmpDM:artist="WAV交響楽団"
(以下データが続く)
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
実際のデータ例
ではここで、実際のwavファイルの中を見てみましょう。
例1 最小構成のwavファイル
『PythonでFFT』の実験で使用した正弦波ファイルです。
位置 | バイナリデータ | 意味 |
---|---|---|
(最初はRIFFチャンク) | ||
00000000~00000003 | 52 49 46 46 | チャンク識別子 ”RIFF” |
00000004~00000007 | AC 58 01 00 | 総データサイズは0x158AC=88,236 bytes (ファイルサイズ88,244 bytes - 8 bytes) |
00000008~0000000B | 57 41 56 45 | データフォーマット “WAVE” |
(ここからfmtチャンク) | ||
0000000C~0000000F | 66 6D 74 20 | チャンク識別子 “fmt “ |
00000010~00000013 | 10 00 00 00 | fmtチャンクのデータサイズは 0x00000010=8 bytes よって次のチャンクは 0x00000014+0x00000010 = 0x00000024から |
00000014~00000015 | 01 00 | 音声の型式は 0x0001 = 1(非圧縮リニアPCM) |
00000016~00000017 | 01 00 | チャンネル数は 0x0001 = 1(モノラル) |
00000018~0000001B | 44 AC 00 00 | サンプリング周波数は 0x0000AC44 = 44100 = 44.1 kHz |
0000001C~0000001F | 88 58 01 00 | 1秒あたりのデータは 0x00015888 = 88,200 bytes (=ブロックサイズ2 bytes×サンプリング周波数44100) |
00000020~00000021 | 02 00 | ブロックサイズは 0x0002 = 2 bytes |
00000022~00000023 | 10 00 | 量子化ビット数は 0x0010 = 16 bits |
(ここからdataチャンク) | ||
00000024~00000027 | 64 61 74 61 | チャンク識別子 “data” |
00000028~0000002B | 88 58 01 00 | dataチャンクのデータサイズは 0x00015888 = 88,200 bytes よって次のチャンクは(あるとすれば) 0x0000002C+0x00015888 = 0x0158B4 から この例ではファイルサイズが88244 bytes = 0x0158B4 なので、 これでファイル終端となります。 |
例2 市販音楽CDのデータ
市販の音楽CDから1トラックをwavファイルとして取り込んでみました。
最小構成のwavファイルではdataチャンクは36バイト目から・音声データ本体は44バイト目からなのですが、一般的なwavファイルではそうとは限らないことが判ります。音声データを読み込むには、RIFFチャンクのサブチャンクを順にたどってdataチャンクを探さなければなりません。
位置 | バイナリデータ | 意味 |
---|---|---|
(最初はRIFFチャンク) | ||
00000000~00000003 | 52 49 46 46 | チャンク識別子 ”RIFF” |
00000004~00000007 | 0E 99 80 02 | 総データサイズは0x0280990E=41,982,222 bytes (ファイルサイズ41,982,230 bytes - 8 bytes) |
00000008~0000000B | 57 41 56 45 | データフォーマット “WAVE” |
(ここからfmtチャンク) | ||
0000000C~0000000F | 66 6D 74 20 | チャンク識別子 “fmt “ |
00000010~00000013 | 10 00 00 00 | fmtチャンクのデータサイズは 0x00000010=8 bytes よって次のチャンクは 0x00000014+0x00000010 = 0x00000024から |
00000014~00000015 | 01 00 | 音声の型式は 0x0001 = 1(非圧縮リニアPCM) |
00000016~00000017 | 02 00 | チャンネル数は 0x0002 = 2(ステレオ) |
00000018~0000001B | 44 AC 00 00 | サンプリング周波数は 0x0000AC44 = 44100 = 44.1 kHz |
0000001C~0000001F | 10 B1 02 00 | 1秒あたりのデータは 0x0002B110 = 176,400 bytes (=ブロックサイズ4 bytes×サンプリング周波数44100) |
00000020~00000021 | 04 00 | ブロックサイズは 0x0004 = 4 bytes |
00000022~00000023 | 10 00 | 量子化ビット数は 0x0010 = 16 bits |
(ここからJUNQチャンク) | ||
00000024~00000027 | 4A 55 4E 51 | チャンク識別子 “JUNQ” |
00000028~0000002B | 26 02 00 00 | dataチャンクのデータサイズは 0x00000226 = 550 bytes よって次のチャンクは 0x0000002C+0x00000226 = 0x00000252 から |
0000002C~00000251 | 略 | JUNQチャンクのデータ 後のLISTチャンクと重複しますが、 曲名やアーティスト名などが格納されています。 |
(ここからJUNKチャンク) | ||
00000252~00000255 | 4A 55 4E 4B | チャンク識別子 “JUNK” |
00000256~00000259 | 9E 0D 00 00 | JUNKチャンクのデータサイズは 0x00000D9E = 3,486 bytes よって次のチャンクは、 0x0000025A + 0x00000D9E = 0x00000FF8 から |
0000025A~00000FF7 | 略 | JUNKチャンクのデータ dataチャンクのデータ本体がキリの良い場所から始まるように するためのダミーのチャンクで、00で埋められています。 |
(ここからdataチャンク) | ||
00000FF8~00000FFB | 64 61 74 61 | チャンク識別子 “data” |
00000FFC~00000FFF | 20 78 80 02 | dataチャンクのデータサイズは 0x02807820 = 41,973,792bytes よって次のチャンクは、 0x00001000 + 0x02807820 = 0x02808820 から |
00001000~0280881F | 略 | dataチャンクのデータ(音声データ本体) ※このトラックは冒頭1秒ほどが無音のため 00 が並んでいます |
(ここから_PMXチャンク) | ||
02808820~02808823 | 5F 50 40 58 | チャンク識別子 “_PMX” |
02808824~02808827 | 18 0E 00 00 | _PMXチャンクのデータサイズは 0x00000E18 = 3,608bytes よって次のチャンクは、 0x02808828 + 0x00000E18 = 0x02809640 から |
02808828~0280963F | 略 | _PMXチャンクのデータ XML型式のデータが格納されています。 製作に使用したシステム情報などのようです。 |
(ここからDISPチャンク) | ||
02809640~02809643 | 44 49 53 50 | チャンク識別子 “DISP” |
02809644~02809647 | 04 00 00 00 | DISPチャンクのデータサイズは 0x00000004 = 4bytes よって次のチャンクは、 0x02809648 + 0x00000004 = 0x0280964C から |
02809648~0280964B | 略 | DISPチャンクのデータ |
(ここからLISTチャンク) | ||
0280964C~0280964F | 4C 49 53 54 | チャンク識別子 “LIST” |
02809650~02809653 | C2 02 00 00 | LISTチャンクのデータサイズは 0x000002C2 = 706bytes よって次のチャンクは(あるとすれば) 0x02809654 + 0x000002C2 = 0x02809916 からですが、 0x02809916 = 41,982,230 でファイル終端となります。 |
02809654~02809915 | 略 | LISTチャンクのデータ LISTチャンクはさらにいくつかのタイプに分かれています。 ここではINFOというタイプです。 LIST_INFOは内部サブチャンクを含み、 音声データに関する情報を格納しています。 LISTチャンク └ INFO ├ ICRDサブチャンク:データ作成年月日 ├ IARTサブチャンク:アーティスト情報 ├ INAMサブチャンク:曲名 など |
サンプルプログラム
以上の情報をもとに、以前の簡易版よりも対応範囲の広いwavファイル読み込みのモジュールを作ってみました。numpy に依存しています。
※適宜改良するかもしれません
import sys
import numpy
# wav ファイルを読み込む
def readfile(filename):
# 元音声の読み込み(16ビットモノラル、サンプリング周波数44.1kHz限定)
f = open(filename, "rb")
# 先頭は RIFF chunk
if f.read(4).decode() != "RIFF":
return False # 0~ 3 "RIFF" でなければwavファイルではないので終了
filesize = int.from_bytes(f.read(4), sys.byteorder)+8 # 4~ 7 ファイルサイズ-8
if f.read(4).decode() != "WAVE":
return False # 8~11 "WAVE" でなければwavファイルではないので終了
# ここから fmt chunk
if f.read(4).decode() != "fmt ":
return False # 12~15 "fmt " でなければwavファイルではないので終了
fmtchunksize = int.from_bytes(f.read(4), sys.byteorder) # 16~19 fmtチャンクのサイズ、デフォルト 0x10
formattype = int.from_bytes(f.read(2), sys.byteorder) # 20~21 フォーマット形式、非圧縮PCMなら0x01
ch = int.from_bytes(f.read(2), sys.byteorder) # 22~23 チャンネル数
samplingfreq = int.from_bytes(f.read(4), sys.byteorder) # 24~27 サンプリング周波数
bytepersec = int.from_bytes(f.read(4), sys.byteorder) # 28~31 1秒あたりのデータのバイト数
blocksize = int.from_bytes(f.read(2), sys.byteorder) # 32~33 ブロックサイズ(1サンプリングあたりのバイト数)
quantizationbits = int.from_bytes(f.read(2), sys.byteorder) # 34~35 1サンプルあたりのビット数
datachunkpos = 20 + fmtchunksize # 36~に拡張データが入る場合がある
f.seek(datachunkpos) # fmt chunk に拡張データがあっても読み飛ばす
# data chunk の先頭を探す
while f.read(4).decode() != "data": # 次のchunk識別子が"data"ではない場合、
skip = int.from_bytes(f.read(4), sys.byteorder) # その次の4バイトがchunkサイズを表すので、
f.read(skip) # その分読んでしまう
datasize = int.from_bytes(f.read(4), sys.byteorder) # 4~ 7 波形データのバイト数
# ここから波形データ本体
if quantizationbits == 8:
data = numpy.fromfile(f, dtype="uint8") # 量子化8ビットなら符号なし8ビット整数
elif quantizationbits == 16:
data = numpy.fromfile(f, dtype="int16") # 量子化16ビットなら符号付き16ビット整数
data = data[0:int(datasize/(quantizationbits/8))] # ファイルの最後まで読むとゴミが入ってるのでdatasize分までで切る
if ch==2:
ldata = data[0:len(data):2] # ch=2 なら左右に分離
rdata = data[1:len(data):2]
else:
ldata = data
rdata = data
return {"ch":ch, "samplingfreq":samplingfreq, "quantizationbits":quantizationbits,
"formattype":formattype, "bytepersec":bytepersec, "blocksize":blocksize,
"ldata":ldata, "rdata":rdata}
# wav ファイルを出力する
# data はnumpy.arrayで、
# モノラルなら [[data1,data2,…]]
# ステレオなら [[ldata1,ldata2,…], [rdata1,rdata2,…]]
# の型式で与える
def writefile(data, filename, ch=1, samplingfreq=44100, quantizationbits=16):
# データのチェックと下準備
if ch == 1:
if len(data) != 1:
return False
else:
fdata = data[0]
elif ch == 2:
if len(data) != 2:
return False
elif len(data[0]) != len(data[1]):
return False
else:
fdata = numpy.empty(len(data[0])*2, dtype=data[0].dtype)
fdata[0:len(fdata):2] = data[0]
fdata[1:len(fdata):2] = data[1]
else:
return False # ch が1,2以外だったらエラー
if quantizationbits == 8:
if data.dtype != "uint8":
return False
elif quantizationbits == 16:
if data.dtype != "int16":
return False
else:
return False # quantizationbitsが8,16以外だったらエラー
blocksize = int(ch*quantizationbits/8) # ブロックサイズ(1サンプルあたりのバイト数)
bytespersec = int(samplingfreq*blocksize) # 1秒あたりのバイト数
headersize = 44 # ヘッダサイズ(wavの最小構成では44バイト)
datasize = int(len(fdata)*quantizationbits/8) # 音声データのバイト数
filesize = datasize+headersize # ファイルサイズ
f = open(filename, "wb")
f.write("RIFF".encode()) # 0- 3 "RIFF"に固定
f.write((filesize - 8).to_bytes(4, sys.byteorder)) # 4- 7 ファイルサイズ-8
f.write("WAVE".encode()) # 8-11 "WAVE"に固定
f.write("fmt ".encode()) #12-15 "fmt "に固定
f.write(0x10.to_bytes(4,sys.byteorder)) #16-19 fmtチャンクのバイト数、デフォルト0x10
f.write(0x01.to_bytes(2,sys.byteorder)) #20-21 音声フォーマット形式、0x01は非圧縮PCM
f.write(ch.to_bytes(2,sys.byteorder)) #22-23 チャンネル数 0x01はモノラル
f.write(samplingfreq.to_bytes(4,sys.byteorder)) #24-27 サンプリングレート44.1kHz
f.write(bytespersec.to_bytes(4,sys.byteorder)) #28-31 1秒あたりのデータのバイト数
f.write(blocksize.to_bytes(2,sys.byteorder)) #32-33 ブロックサイズ(1サンプリングあたりのバイト数)
f.write(quantizationbits.to_bytes(2,sys.byteorder)) #34-35 1サンプルあたりのビット数
f.write("data".encode()) #36-39 "data"に固定
f.write(datasize.to_bytes(4,sys.byteorder)) #40-43 データサイズ
fdata.tofile(f) #44- データ本体
f.close()
コメント