#オーディオ・音声分析への道 その3 Libsndfile
今回は演算結果をオーディオデータとして出力したいと思います。
オーディオデータをC言語で扱う為のライブラリはいくつかあると思いますが、ここではLibsndfileを使いたいと思います。
Libsndfile はオーディオデータを扱う為のCライブラリです。データを読み込んだり、ファイルに書き出す事ができます。
対応しているオーディオフォーマットは沢山ありますが、中でもWAVやAIFF、RAW、OGGに対応しています。
ではLibsndfileのダウンロード、Xcodeへの追加設定を説明します。
Macportのインストール
LibsndfileのMacへのインストールはMacportを使うと便利です。
Macportは、Macへのソフトウェア導入を簡略化してくれるパッケージ管理システムです。
ここからファイルをダウンロードします。(英語サイト)
2.2.1. Mac OS X Package Installから、自分のOSにあうパッケージをダウンロードします。
筆者はMavericksなので、MacPorts-2.3.0-10.9-Mavericks.pkgをダウンロードします。
インストールが完了したら、ターミナルを起動して確認をします。
ターミナルに
$ 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のプロジェクトに追加します。
(インストールされたファイルを移動せず、そのままリンクします。)
これで準備完了です。
##sin波を生成してWAVファイルに書き出す
さて、いよいよプログラミングをしましょう。
まずは、sin波を生成して、それをWAVファイル(ステレオ, 16bit, 44100Hz)に書き出してみたいと思います。
では必要なヘッダをインクルードします。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
ここで、Libsndfileのインクルードは以下の様にします。
#import"sndfile.h"
次に、sin波を生成する為に必要な情報を定義しておきます。
#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型構造体を使用します。
これは以下の様に宣言します。
SNDFILE *fp;
また、オーディオデータの各種設定はSF_INFO型の構造体に格納します。今回とは逆に、オーディオデータを読み込む場合は、SF_INFO型構造体に読み込んだオーディオデータの情報が格納される事になります。
SF_INFO sfinfo;
念のため、sfinfoの中身を初期化します。
memset(&sfinfo,0,sizeof(sfinfo));
sfinfoに必要なデータを格納します。
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数はとりあえず以下の四種類を知っておけば良いです。
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波生成用のメモリ領域を確保します。
float *buffer;
buffer = malloc(2*SAMPLE_RATE*LENGTH*sizeof(float));
データ型はfloatです。
ここで、bufferのサイズは、
2(チャンネル数) x (サンプリングレート) x オーディオデータの長さ x float型データサイズ
となります。一秒間に44100の解像度で標本化されるので、44100個のデータがチャンネル毎に格納されます。
C言語でファイルを生成する場合は **fopen()** を使用しますが、Libsndfileの場合は **sf_open()** を使用します。
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に格納します。
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データファイルとして書き出します。
if(sf_write_float(fp, buffer,sfinfo.channels * SAMPLE_RATE*LENGTH)!= sfinfo.channels * SAMPLE_RATE*LENGTH)
puts(sf_strerror (fp));
sf_write_float()を使用します。
これは用途によって、以下の種類が用意されています。
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 をクローズして終わりです。
sf_close(fp);
全体のコードです。
#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等のフリーソフトでオーディオデータを開いてみると、波形が確認できます。
波形を拡大すると、このように見えます。
Libsndfileの使い方のみになりましたが、次回はvDSPと絡めていきます...