Waveファイルの生成

Waveフォーマット

ファイルの先頭から順に「RIFFヘッダ」,「fmtチャンク」,「dataチャンク」と呼ばれるメモリ領域に分かれています。
各チャンクはそれぞれ識別子とチャンクのサイズ、データと分かれており、波形データはdataチャンクのデータ部分に記録されています。

fmtチャンク: 定義 チャンクサイズ データ
dataチャンク: 定義 チャンクサイズ データ

各領域の詳細を構造体として眺めてみると次のようになります。

waveデータを扱う構造体(波形データ以外)

//波形data以外のすべてのheader情報
typedef struct WaveHeader
{
	//RIFF header
	unsigned char   RiffTag[4];	//"RIFF"という文字列(4byte)
	unsigned long    SizeOfFile;	//total file size - 8を指定する(4byte)
	unsigned char   WaveTag[4];	// "WAVE"という文字列(4byte)
	//fmt Chank header Section
	unsigned char   FmtTag[4];	//"fmt "という文字列,4文字目が空白(4byte)
	unsigned long    SizeOfFmt;	//fmtチャンクのサイズ(4byte)
	//fmt Chank data Section
	struct WaveFormat
	{
		unsigned short  FormatID;        //リニアPCMの場合は1(2byte)
		unsigned short  Channels;        //チャネル数(2byte)
		unsigned long   SamplingRate;    //サンプリング周波数Hz(4byte)
		unsigned long   BytesPerSec;     //レート(4byte)
		unsigned short  BlockAlign;      //1ブロックあたりバイト数 (2byte)
		unsigned short  BitsPerSample;   //1サンプルあたりビット数 (2byte)
	}WaveFormat;
	//Data Chank header Section
	unsigned char   DataTag[4];	//'data' (4byte)
	unsigned long   SizeOfBuffer;	//波形データのトータルサイズ (4byte)
}WaveHeader;

"普通"のwaveファイルを扱うために必要な情報を整理してみます。

値が定まっている変数

RiffTag,WaveTag,fmtTag,DataTagは固定4文字
SizeOfFmt = 16;//(16 byte)
FormatID = 1;
ヘッダの総サイズ 44byte

必要な変数

Waveの仕様と、音声データの性質を決定する変数です。
Channels = 2;//Stereo
SamplingRate;//44100Hz
BitsPerSample;//16bit

上記3つより計算できる値

残りのフォーマットについては上の3つから計算することができるので、一緒に記憶する必要はありません。
レート
BytesPerSec = SamplingRate * BytesPerSample * Channels;
1ブロックあたりバイト数
BlockAlign = BitsPerSample / 8 * Channels;
ちなみに、1サンプルあたりバイト数
BytesPerSample = BitsPerSample / 8;

波形データのサイズに依存する変数

SizeOfBufferを波形データのbyte数とします。ファイルサイズ部分は次のようにして得られます。
SizeOfFile = SizeOfBuffer + sizeof(WaveHeader)-8; (データ総byte + 44 -8)
残りの波形データについて確認してみます。

波形データの構造

波形データはモノラルの場合配列そのままに、ステレオの場合はLRが交互に格納されています。

mono sample1 sample2 sample3 ...
stereo sample1(L) sample1(R) sample2(L) sample2(R) ...

1サンプルの値は、サンプルあたりのbit数によって少し異なります。
例えば、
8bitの場合1サンプル1byte unsigned char型0〜255 128が無音
16bitの場合1サンプル2byte short型 -32768〜32767 0が無音
という情報が記録されています。

サンプル数(チャネルごと)と波形データサイズの変換式

SizeOfSample個のデータを作りたい場合に必要なバッファサイズ(byte)は次のようにして求められます。
SizeOfBuffer = SizeOfSample * (BitsPerSample / 8) * Channels;
Cではmallocでこのようにメモリを確保すると良いと思います。

void* buffer=malloc(SizeOfBuffer);

確保した領域の先頭アドレスからi番目のサンプルの値を取得するには、サンプルあたりのbit数によってアクセスの仕方が異なります。
モノラル

unsigned char *ptr=(unsigned char*)buffer;  //2byte=16bitごとにアクセス
for(unsigned long i=0;i<SizeOfSample;i++){
//ptr[i]    //i番目のサンプル
}

ステレオ

short *ptr=(short*)buffer;  //2byte=16bitごとにアクセス
for(unsigned long i=0;i<SizeOfSample;i++){
//ptr[i*2]    //左チャネルi番目のサンプル
//ptr[i*2+1]  //右チャネルi番目のサンプル
}

秒とサンプル数(チャネル毎)・バッファサイズの変換

ある長さの時間(秒)のデータに必要なサンプル数は次のようにして求めることができます。
float Duration;//波形データの長さ[秒]
unsigned long SizeOfSample=(SamplingRate*Duration); //sample数
バッファサイズは前述のとおりです。

周期関数の生成

振幅A:[0,1],周波数f[Hz]の周期関数A\cdot s(2\pi ft)をサンプリング周波数f_sで標本化する場合について考えてみます。
1[s]をf_s分割するので,1サンプルあたりの間隔は\Delta t=1/f_sです。
周期T[s]としましょう。
t:[0,T]i番目の時刻t_it_i=i \Delta t =i / f_sです。
ちなみに周期T[s]なら総サンプル数はN=T/\Delta t=T f_sになります。
標本化した信号の値はy_i = A\cdot s(2\pi f i\Delta t)=A\cdot s(2\pi i\frac{f}{f_s})になります
サンプリング周波数44100Hz,2ch,16bit/サンプルのフォーマットで、キーが"A"の正弦波(440Hz)3秒分のバッファを作成する例です。

float SamplingRate = 44100.0f; //[Hz]
float Frequency = 440.0f; //[Hz]
float Amplification = 0.6f;
float Duration=3.0f;//[s]
const double PI = 3.1415926535;
unsigned long SizeOfSample=unsigned long (SamplingRate*Duration);//sample数の計算

double val;
unsigned long i;
for(i = 0;i < SizeOfSample;i++){
	val= Amplification*sin(2 * PI * i * Frequency / SamplingRate);
	ptr[i*2] = short(val*32767);	//L channel
	ptr[i*2+1] = short(val*32767);	//R channel
}