[vDSP][信号処理]オーディオ・音声分析への道3 Libsndfile

#オーディオ・音声分析への道 その3 Libsndfile

今回は演算結果をオーディオデータとして出力したいと思います。
オーディオデータをC言語で扱う為のライブラリはいくつかあると思いますが、ここではLibsndfileを使いたいと思います。

[Libsndfile](http://www.mega-nerd.com/libsndfile/) はオーディオデータを扱う為のCライブラリです。データを読み込んだり、ファイルに書き出す事ができます。
対応しているオーディオフォーマットは沢山ありますが、中でもWAVやAIFF、RAW、OGGに対応しています。

ではLibsndfileのダウンロード、Xcodeへの追加設定を説明します。

## Macportのインストール

LibsndfileのMacへのインストールはMacportを使うと便利です。
Macportは、Macへのソフトウェア導入を簡略化してくれるパッケージ管理システムです。
[ここから](https://guide.macports.org/chunked/installing.macports.html)ファイルをダウンロードします。(英語サイト)

2.2.1. Mac OS X Package Installから、自分のOSにあうパッケージをダウンロードします。
筆者はMavericksなので、MacPorts-2.3.0-10.9-Mavericks.pkgをダウンロードします。

![Libsndfile01.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/f3140d07-69ff-87ff-71d8-ef9c011aa505.jpeg)

インストールが完了したら、ターミナルを起動して確認をします。

![libsndfile02.png](https://qiita-image-store.s3.amazonaws.com/0/46509/b661245f-993f-1928-ec73-d912279ab1f1.png)

ターミナルに 

```
$ port version
```

と打ち、returnターン!!
ちなみにportとはMacPortのPortですね。

```
Version: 2.3.0
```

上のようにVersionが表示されればOKです。

ここで、念のためMacportを最新バージョンにアップデートしておきます。
この為のコマンドは、

```
$ sudo port -d self update
```

とします。すると、パスワードを尋ねられるので、パスワードを入力してreturn!
するとターミナルにログが表示されアップデートが始まります。最後に、

```
The ports tree has been updated. To upgrade your installed ports, you should run port upgrade outdated
```

と表示されればOKです。

##Libsndfileのインストール

さぁ、ここからlibsndfileのインストールです。
ターミナルに、

```
$ sudo port install libsndfile
```

とします。もしパスワードを尋ねられれば、また入力します。
universalモード（intel MacとG5, G4Mac両用）でインストールする場合は、

```
$ sudo port install libsndfile +universal
```

とします。


##Xcodeの設定

Xcodeに必要なインストールされたLibsndfileのファイルは三つあり、

+ sndfile.h (C言語のヘッダ) : 場所 Macintosh HD/opt/local/include/sndfile.h
+ sndfile.hh(C++のヘッダ) : 場所 Macintosh HD/opt/local/include/sndfile.hh
+ libsndfile.a(ライブラリ) : 場所 Macintosh HD/opt/local/include/libsndfile.a

となります。
上記三つのファイルをXcodeのプロジェクトに追加します。
(インストールされたファイルを移動せず、そのままリンクします。)


![Libsndfile03.png](https://qiita-image-store.s3.amazonaws.com/0/46509/ce8b55a1-501c-9450-a143-3ba7f07c8107.png)

これで準備完了です。

##sin波を生成してWAVファイルに書き出す

さて、いよいよプログラミングをしましょう。
まずは、sin波を生成して、それをWAVファイル(ステレオ, 16bit, 44100Hz)に書き出してみたいと思います。

では必要なヘッダをインクルードします。

```C:main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
```

ここで、Libsndfileのインクルードは以下の様にします。

```c:main.c
#import"sndfile.h"
```

次に、sin波を生成する為に必要な情報を定義しておきます。

```c:main.c
#define PAI 3.14159265358979323846264338 // PAI
#define SAMPLE_RATE 44100 //sampling rate
#define LENGTH 4 // 4 seconds
#define AMPLITUDE 1.0
#define FREQUENCY 440 // frequency Hz
```

PAIはsin波生成で使用する円周率、サンプリングレートは44.1kHz, オーディオデータの長さは4秒, 音の大きさAMPLITUDEレンジはデータタイプがfloat型の場合は1.0です。FREQUENCYは生成するsin波の周波数を指定します。

Libsndfileでオーディオデータを使用する際には、C言語のFILE型の構造体を使うのではなく、Libsndfile準拠のSNDFILE型構造体を使用します。
これは以下の様に宣言します。


```c:main.c
SNDFILE *fp;
```

また、オーディオデータの各種設定はSF_INFO型の構造体に格納します。今回とは逆に、オーディオデータを読み込む場合は、SF_INFO型構造体に読み込んだオーディオデータの情報が格納される事になります。


```c:main.c
SF_INFO sfinfo;
```

念のため、sfinfoの中身を初期化します。

```c:main.c
memset(&sfinfo,0,sizeof(sfinfo));
```

sfinfoに必要なデータを格納します。


```c:main.c
sfinfo.samplerate = SAMPLE_RATE;
sfinfo.frames = SAMPLE_RATE*LENGTH;
sfinfo.channels = 2;
sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_16);
```

ここれ、sfinfo.formatにはオーディオデータの種類と、bit数を設定します。
WAVデータの場合は **SF_FORMAT_WAV** とし、AIFFデータの場合は **SF_FORMAT_AIFF** 等とします。
bit数はとりあえず以下の四種類を知っておけば良いです。

```c:main.c
SF_FORMAT_PCM_S8= 0x0001,		/* Signed 8 bit data */
SF_FORMAT_PCM_16= 0x0002,		/* Signed 16 bit data */
SF_FORMAT_PCM_24= 0x0003,		/* Signed 24 bit data */
SF_FORMAT_PCM_32= 0x0004,		/* Signed 32 bit data */
```

次にsin波生成用のメモリ領域を確保します。

```c:main.c
float *buffer;
buffer = malloc(2*SAMPLE_RATE*LENGTH*sizeof(float));
```

データ型はfloatです。
ここで、bufferのサイズは、
	2(チャンネル数) x (サンプリングレート) x オーディオデータの長さ x float型データサイズ
となります。一秒間に44100の解像度で標本化されるので、44100個のデータがチャンネル毎に格納されます。

	C言語でファイルを生成する場合は **fopen()** を使用しますが、Libsndfileの場合は **sf_open()** を使用します。

```c:main.c
if(!(fp = sf_open("SinWave.wav", SFM_WRITE, &sfinfo))){
		printf("Error : Could not open output file .\n");

		return 1;
	}
```

第一引数にファイルのアドレス、
	第二引数にオープンのタイプ
+ SFM_READ // 読み込み
+ SFM_WRITE // 書き込み
+ SFM_RDWR	// 追加

第三匹数にsfinfoを入れます。
ファイルの生成に失敗するとNULLを返します。

次にsin波を計算してbufferに格納します。


```c:main.c
for(i=0;i<SAMPLE_RATE*LENGTH;i++){ // STEREO
        //440hz
	buffer[2*i] = AMPLITUDE * sin((float)FREQUENCY / SAMPLE_RATE * 2 * PAI * i);
        //880hz
	buffer[2*i+1] = AMPLITUDE * sin((float)FREQUENCY*2 / SAMPLE_RATE * 2 * PAI * i);	
	}
```

今回は、1チャンネルに440hzのsin波を、2チャンネルに二倍の880hzのsin波を入れました。左右で音高が変わります。

最後に、wavデータファイルとして書き出します。


```c:main.c
if(sf_write_float(fp, buffer,sfinfo.channels * SAMPLE_RATE*LENGTH)!= sfinfo.channels * SAMPLE_RATE*LENGTH)
	puts(sf_strerror (fp));
```

sf_write_float()を使用します。
これは用途によって、以下の種類が用意されています。

```c:main.c
sf_count_t sf_read_short	(SNDFILE *sndfile, short *ptr, sf_count_t items) ;
sf_count_t sf_write_short	(SNDFILE *sndfile, const short *ptr, sf_count_t items) ;
sf_count_t sf_read_int		(SNDFILE *sndfile, int *ptr, sf_count_t items) ;
sf_count_t sf_write_int 	(SNDFILE *sndfile, const int *ptr, sf_count_t items) ;
sf_count_t sf_read_float	(SNDFILE *sndfile, float *ptr, sf_count_t items) ;
sf_count_t sf_write_float	(SNDFILE *sndfile, const float *ptr, sf_count_t items) ;
sf_count_t sf_read_double	(SNDFILE *sndfile, double *ptr, sf_count_t items) ;
sf_count_t sf_write_double	(SNDFILE *sndfile, const double *ptr, sf_count_t items) ;
```

**sf_strerror** にはエラーメッセージが格納されます。必要に応じてコンソールに出力します。
	
最後に **fp** をクローズして終わりです。

```c:main.c
sf_close(fp);
```

全体のコードです。


```c:main.c
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#import"sndfile.h"
#define PAI 3.14159265358979323846264338 // PAI
#define SAMPLE_RATE 44100 //sampling rate
#define LENGTH 4 // 4 seconds
#define AMPLITUDE 1.0// 16bit
#define FREQUENCY 440 // frequency Hz

int main(void)

{
	SNDFILE *fp;
	SF_INFO sfinfo;
	int i;

	float *buffer;
	buffer = malloc(2*SAMPLE_RATE*LENGTH*sizeof(float));

	//initialization
	memset(&sfinfo,0,sizeof(sfinfo));

	sfinfo.samplerate = SAMPLE_RATE;
	sfinfo.frames = SAMPLE_RATE*LENGTH;
	sfinfo.channels = 2;
	sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_16);

	if(!(fp = sf_open("SinWave.wav", SFM_WRITE, &sfinfo))){
		printf("Error : Could not open output file .\n");
		return 1;
	}

	for(i=0;i<SAMPLE_RATE*LENGTH;i++){ // STEREO	
        //440hz
	buffer[2*i] = AMPLITUDE * sin((float)FREQUENCY / SAMPLE_RATE * 2 * PAI * i);
        //880hz
	buffer[2*i+1] = AMPLITUDE * sin((float)FREQUENCY*2 / SAMPLE_RATE * 2 * PAI * i);	
	}

	if(sf_write_float(fp, buffer,sfinfo.channels * SAMPLE_RATE*LENGTH)!= sfinfo.channels * SAMPLE_RATE*LENGTH)
		puts(sf_strerror (fp));

	sf_close(fp);

	return 0;
}
```

ビルド・実行すると、SinWave.wavというファイルが出力されます。
[Audacity](http://audacity.sourceforge.net/?lang=ja)等のフリーソフトでオーディオデータを開いてみると、波形が確認できます。

波形を拡大すると、このように見えます。

![libsndfile04.png](https://qiita-image-store.s3.amazonaws.com/0/46509/7a618cd5-8c27-7529-50e4-7a77a46e98ee.png)

Libsndfileの使い方のみになりましたが、次回はvDSPと絡めていきます...