はじめに
ここでは音声ファイルであるWAVファイル(.wav)のフォーマットの説明と簡単な読込サンプルについて解説しています。
WAVファイルについては、「wav フォーマット」などで検索するとすでに他のサイトでも解説されていますので、簡単に説明します。
WAVファイルのフォーマット
WAVファイルはRIFFファイル形式をとっています。
RIFFファイル形式では、そのファイルに必要なデータをチャンクと呼ばれるブロックに分けて保存し、チャンクにFourCCと呼ばれる4Byteの識別子(ID)を付けて管理するファイル形式です。
RIFFファイルについては下記に簡単に記載しております。必要に応じてご参照ください。
今回作成するWAVファイルの読み込み関数のサンプルはこちらからダウンロードできます。 → riffsample_20231210
※Wavファイル以外にもSoundfontファイル読込サンプルなども同梱されています。
WAVファイルのチャンク
WAVファイルのチャンクと階層構造は下記となっています。各チャンクの詳細は後述します。
2つの必須チャンクがあります。
チャンク名 | タイプ | 必須 /任意 |
チャンク識別子 (ファイル/LIST識別子) |
概要 |
---|---|---|---|---|
RIFFチャンク | RIFF | 必須 | RIFF(WAVE) | RIFFチャンク、ファイル識別子は「WAVE」となる。 |
fmt チャンク | 一般 | 必須 | fmt (「fmt 」の最後に空白があるので注意) |
音声波形データ(後述のdataチャンク)のデータ形式情報が保存されたチャンク |
dataチャンク | 一般 | 必須 | data | 音声波形データが保存されたチャンク |
WAVファイルの各チャンクの説明
WAVファイルの各チャンクについて説明いたします。
fmtチャンク
概要 | 音声波形データ(後述のdataチャンク)のデータ形式情報が保存されたチャンクになります | |||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
必須・任意 | 必須 | チャンクサイズ | 16Byte固定 | |||||||||||||||||||||||
データ部 | データ部には下記 構造体が入ります。
(WORD型はunsigned short、DWORD型はunsinged longの再定義となります。)
|
dataチャンク
概要 | 音声波形データが保存されたチャンクになります。 | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
必須・任意 | 必須 | チャンクサイズ | 音声波形データのサイズになります | ||||||||||||||||||||||||||||
データ部 | データ部は、fmtチャンクのformatid・chunnelnum・bitrateの内容によって変わります。 formatidが「1」(圧縮されていない音声波形)の場合、下記のようになります。 【例】
|
WAVファイル読み込みサンプル
WAVファイルの読み込みの流れは
- fmtチャンクを読み込む
- fmtチャンクの内容に合わせてdataチャンクの内容を読み込む
となります。
下記に「44,100Hz 16bit 非圧縮 モノラルorステレオ」のWAVファイルを読み込む関数のサンプルコードを記載します。
説明についてはコード内に記載しているので割愛します。
なお、WAVファイルの読み込みには下記のRIFFの読込を行うクラスを使用していますので、下記もご参照ください。
WAVファイルの読み込み関数のサンプルはこちらからもダウンロードできます。 → riffsample_20231210
※Wavファイル以外にもSoundfontファイル読込サンプルなども同梱されています。
【readWavFile.h】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
#pragma once #include <vector> using namespace std; // wavファイルはRIFF形式のファイルのため、RIFFの読込を行うクラスを使用する。 // RIFF読込クラスについては「https://vstcpp.wpblog.jp/?p=2181」 // (criffloader.hではWORD・DWORDなどを定義しているので、windows.hを利用する場合は注意すること!) #include "criffloader.h" // --------------------------------------------------------------------- // 波形データを格納するため型を定義 using wavbuffer = vector<short>; // .wavファイル用の構造体。fmt チャンクに記載された内容をそのまま読み込める形式となっている struct MyWAVEHEADER { WORD formatid; // データ部分のフォーマット。「1」(リニアPCM)以外は圧縮等がされている WORD chunnelnum; // チャンネル数。mono…1、stereo…2となる DWORD samplerate; // サンプルレート。44100Hzなら44100となる DWORD datarate; // 「samplerate * (bitrate / 8bit) * chunnelnum」で算出する(「samplerate *blocksize」でも可) WORD blocksize; // 「(bitrate / 8bit) * chunnelnum」で算出する WORD bitrate; // ビットレート。16bitなら16となる }; // .wavファイル用のチャンクIDを定義する FOURCC fccWAVE = FourCC('W', 'A', 'V', 'E'); FOURCC fccfmt = FourCC('f', 'm', 't', ' '); FOURCC fccdata = FourCC('d', 'a', 't', 'a'); // .wavファイルを読み込む関数 // 44,100Hz 16bit 非圧縮 モノラルorステレオにのみ対応。 // 戻り値:ファイルの読込に成功するとtrue、失敗するとfalseを返す // 引数: // filename … 読み込む.wavファイルのパスを指定する // wavL,wavR … 読み込まれた.wavファイルの波形データが格納される(short型の動的配列(vector<short>)) // chunnelnum … 読み込まれた.wavファイルのチャンネル数が格納される // samplerate … 読み込まれた.wavファイルのサンプリングレートが格納される // // 【使い方】 // 下記のように使います。 // char filename[] = "test.wav"; // wavbuffer wavL; // wavbuffer wavR; // short chunnelnum; // int samplerate; // // readWavFile(filename, &wavL, &wavR, &chunnelnum, &samplerate); // bool readWavFile(char filename[], wavbuffer* wavL, wavbuffer* wavR, short* chunnelnum, int* samplerate) { // ----------------------------- // まずはファイルを開く。 CRiffLoader riffloader(filename); if (riffloader.getFileSize() == 0) { return false; } // ファイルの読込に失敗した場合の対応 // ----------------------------- // 次に.wavファイルの情報が記載されたfmt チャンクを読み込む。 MyWAVEHEADER myWH; // fmt チャンクのサイズを確認。16出なければエラーとする。 if (riffloader.getChunkSize(fccfmt) != 16) { return false; } // fmt チャンクのデータを読み込む。 if (riffloader.getChunkData(fccfmt, &myWH, 16) == 0) { return false; } // 読み込んだfmt チャンクの内容を確認 // エラー処理(44,100Hz 16bit モノラルorステレオ以外はエラーとする。 if (myWH.samplerate != 44100) { return false; } if (myWH.bitrate != 16) { return false; } if (myWH.chunnelnum != 1 && myWH.chunnelnum != 2) { return false; } // サンプリングレートとチャンネル数をそれぞれのひきすうに格納 (*samplerate) = (int)myWH.samplerate; (*chunnelnum) = myWH.chunnelnum; // ----------------------------- // 最後にdataチャンク(波形データ)を読み込む // dataチャンクのサイズを確認。 int datasize = riffloader.getChunkSize(fccdata); if (datasize == 0) { return false; } // 波形データを読み込むためのバッファを確保する int bytes = myWH.bitrate / 8; // 波形データ 1サンプルあたりのバイト数(16bitのはずなので2となる) short* tmpbuf = new short[datasize / bytes]; // dataチャンクのデータを読み込む if (riffloader.getChunkData(fccdata, tmpbuf, datasize) == 0) { // データの読込に失敗 delete[] tmpbuf; return false; } // 波形データ用のバッファをいったんクリア wavL->clear(); wavR->clear(); // dataチャンクの内容をwavLとwavRに格納する int loops = (datasize / bytes) / myWH.chunnelnum; for (int i = 0; i < loops; i++) { if (myWH.chunnelnum == 1) // モノラルの場合 { // 右チャンネル・左チャンネルの両方に同じデータを格納する wavL->push_back(tmpbuf[i]); wavR->push_back(tmpbuf[i]); } else // ステレオの場合 { // dataチャンクのデータは左チャンネル・右チャンネルの順で // 交互に保存されている // それぞれのチャンネルのデータを格納する wavL->push_back(tmpbuf[i * myWH.chunnelnum]); wavR->push_back(tmpbuf[i * myWH.chunnelnum + 1]); } } delete[] tmpbuf; return true; } |
上記の関数ででWAVファイルを読み込むことができます。
VSTなどで利用するのであれば、下記のようにして32bit浮動小数点型(float)などに変換します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
char filename[] = "test.wav"; wavbuffer wavL; // wavbuffer型はvector<short>の再定義 wavbuffer wavR; short chunnelnum; int samplerate; readWavFile(filename, &wavL, &wavR, &chunnelnum, &samplerate); // エラー無く読み込めれば、wavL・wavRには音声波形がshort型で保存される。 // VSTなどで使用するため、wavL・wavRをfloat型(-1.0~1.0の範囲)に変換する。 vector<float> wavLfloat; vector<float> wavRfloat; for (int i = 0; i < wavL.size(); i++) // wavLとwavRのサイズは同じ前提 { wavLfloat.push_back((float)wavL[i] / (float)SHRT_MAX); wavRfloat.push_back((float)wavR[i] / (float)SHRT_MAX); } |
最後に
WAVのファイルフォーマットとそれぞれのチャンクについての説明は以上になります。
今回作成したWAVファイルの読み込み関数のサンプルはこちらからダウンロードできます。 → riffsample_20231210
※Wavファイル以外にもSoundfontファイル読込サンプルなども同梱されています。
他のファイルの読み込みなどはリンクはこちらにまとめております → メディアファイルの取り扱い
質問はコメント欄や掲示板、Twitterでいただけばとおもいます。
また、「この部分を詳しく」などの要望も掲示板やTwitterでいただければと思います。
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp