portsfの仕様と導入

May 22, 2020

ライブラリの導入方法と仕様について説明する. ライブラリはソースのみなので自分でコンパイルする必要がある.

ライブラリは4つのソースコードを持つ.

ライブラリの導入

ソースファイルに直接インクルードしてしまうのが手っ取り早いが, 小さなプログラムをたくさん作る場合実行ファイルが無駄に嵩んでしまう. よって動的リンクをしたい.

ライブラリのビルド, インクルード, リンクパスなどは手動で行う必要がある. その前提知識などはhttps:/zaief.jp/cpp/library2 に説明している. 今回Mac向けに説明する. Linuxの場合はMakefileが付属しているのでそれをそのまま使える.


export CPATH=$CPATH:$HOME/include
export LIBRARY_PATH=$LIBRARY_PATH:$HOME/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/lib
cd ~
git clone https://github.com/bzgeb/portsf.git
cd portsf
ln -sf $HOME/portsf/include/portsf.h $HOME/include/
cd portsf
gcc -c portsf.c
gcc -c ieee80.c
gcc -dynamiclib -o libportsf.dylib portsf.o ieee80.o
ln -sf $HOME/portsf/portsf/libportsf.dylib $HOME/lib/

インクルードできない場合ターミナルを再読み込みなどして環境パスが反映されるようにする.

psf_channelformat

speaker formatに対応する. STDWAVEで大体OK.


typedef enum { STDWAVE, MC_STD, MC_MONO, MC_STEREO, MC_QUAD,
MC_LCRS, MC_BFMT, MC_DOLBY_5_1, MC_WAVE_EX } psf_channelformat;

enum宣言の仕方はCとC++で違いが少しあり, Cではこのようにtypedefを使うことでenum psf_channelformatとせずにpsf_channelformatだけで型として使えた. しかしC++ではこれをせずとも一度enumを宣言してしまえば型として使えるようになる. 上の構文は無名enumをtypedefしている. structも同様なことが言える. 参考: http://www7b.biglobe.ne.jp/~robe/cpphtml/html01/cpp01076.html, https://minus9d.hatenablog.com/entry/20130504/1367634879

psf_format


typedef enum { 
  PSF_FMT_UNKNOWN = 0
  PSF_STDWAVE,
  PSF_WAVE_EX,
  PSF_AIFF,
  PSF_AIFC
} psf_format

psf_stype


typedef enum {
  PSF_SAMP_UNKNOWN = 0,
  PSF_SAMP_8, // NOT YET SUPPORTED!
  PSF_SAMP_16,
  PSF_SAMP_24,
  PSF_SAMP_32,
  PSF_SAMP_IEEE_FLOAT
} psf_stype;

psf_props

サウンドファイルが読み込まれると, 自動的にPSF_PROPSに値が入力される. srateはサンプルレートのことだが, frameレートという用語もありこれはチャンネル全部のサンプルをまとめて一単位とする数え方である. chformatはPSF_WAVE_EXの場合のみ関係する.


typedef struct psf_props
{
  long srate;
  long chans;
  psf_stype samptype;
  psf_format format;
  psf_channelformat chformat;
} PSF_PROPS;


初期化

portsfの機能を使うにはまずint psf_init(void);を呼び出さなくてはならない. 終了するにはint psf_finish(void);を呼び出さなくてはならない. いずれも0を返せば成功.

読み込み


int psf_sndOpen(const char *path, PSF_PROPS *props, int rescale);

wavファイルを開く. なければ新しく生成することはない. 読み込んだファイルを直接変更することもない(READ ONLY). rescaleはサンプルがfloatデータのときに, PEAKチャンクが存在するときのみ意味をなし, 0以外の値ならPEAKチャンクで設定したサンプルのラウドネスを元に-1.0から1.0のレンジに変換するという意味. 0ならそのままのサンプルの値を使う. 戻り値はsound file descriptorと呼ばれ, 識別子の役割を果たす. 0以上の値なら正常. zero未満はエラーで様々なエラーコードが設定されている.

例1


PSF_PROPS props;
int sf;
sf = psf_sndOpen("sample.wav", &props,0);
if(sf < 0) {
  printf("Error: unable to open soundfile\n");
  retur n1;
}

printf("Sample rate = %d\n", props.srate);
printf("number of channels = %d\n", props.chans);

例2


int check_sampletype(psf_stype type)
{
  int accept = 1;
  printf("sampletype: ");
  switch(type) {
    case(PSF_SAMP_8):
      printf("8 bit\n");
      accept = 0; /* No 8bit files! */
      break;
    case (PSF_SAMP_16):
      printf("16 bit\n");
      break;
    case (PSF_SAMP_24):
      printf("24 bit\n");
      break;
    case (PSF_SAMP_32):
      printf("32bit (integer)\n"):
      break;
    case (PSF_SAMP_IEEE_FLOAT):
      printf("32bit floating point\n")
      break;
    default:
      printf("unknown\n");
      accept = 0;
    }

    return accept;
}

サウンドファイルの生成

次の関数を使う.


int psf_sndCreate(const char *path, const PSF_PROPS *props,
                  int clip_floats, int minheader, int mode);

PSF_PROPS *propsはconst入力として使う, つまり今回は事前にpropsの値を定義してから生成する.

clip_floatsはWAVファイルのPEAKチャンクに対応し, floatは-1.0から1.0までの値で解釈するためこの範囲外の値をどう処理するかをこの値で決める. 1ならデフォルトでクリップする, 0ならクリップしない, つまりPEAKチャンクを利用する. ただ読み込み時にPEAKチャンクをちゃんと処理しないソフトも当然あるので基本的に1が妥当.

minheaderを1にすることでヘッダーを最小限にする.

modeは次のものがある. しかしながらサポートされているのはPSF_CREATE_RDWRのみ.


typedef enum {PSF_CREATE_RDWR, PSF_CREATE_TEMPORARY,
            PSF_CREATE_WRONLY } psf_create_mode;


int ofd;
PSF_PROPS props;
props.srate = 96000;
props.chans = 6;
props.samptype = PSF_SAMP_24;
props.format = PSF_WAVE_EX;
props.chformat = MC_DOLBY_5_1;
ofd = psf_sndCreate("soundtrack.wav",&props,1,0,PSF_CREATE_RDWR);
if(ofd < 0) {
  printf("Error: unable to create output file\n");
  return 1;
}


ファイル名からフォーマット取得


psf_format format;
format = psf_getFormatExt("soundtrack.wav");
props.format = format

閉じる


int psf_sndClose(int std);

stdはpsf_sndOpen()で取得したdescripterを入れる.

PEAKチャンクの設定

PEAKチャンクは基本的にない方がいい. ソフトが対応していないから. それでも説明は入れておく.

psf_chpeakでは, posで指定されたサンプルをピークとして設定する.


typedef struct psf_chpeak {
  float val;
  unsgined int pos;
} PSF_CHPEAK

long psf_sndReadPeaks(int sfd, PSF_CHPEAK peakdata[], long *peaktime);

peakdata[]では少なくともチャンネル数だけの領域を確保しておく. peaktimeは大体いらないのでNULLとするのが良い.


PSF_CHPEAK* peaks;
peaks = (PSF_CHPEAK*) malloc(props.chans * sizeof(PSF_CHPEAK));
long peaks_valid = psf_sndReadPeaks(ofd, peaks, NULL);

サンプルデータの読み書き

ファイルフォーマット自体の操作は色々あったが結局実質的なデータはこのサンプルデータなのだから, ここさえ分かればいい. sfd(sound formata descripter)を取得すればokだということだ.


long psf_sndReadFloatFrames(int sfd, float *buf, DWORD nFrames);
long psf_sndWriteFloatFrames(int sfd, const float *buf, DWORD nFrames);

名前からもう直ぐわかる. DWORDはDouble Wordでwindowsが良く使う4bytesの表現, 32bit長. 戻り値は実際に読み書きしたフレーム数だがエラーの場合-1となる.


float* frame = (float*) malloc(props.chans * sizeof(float));
long framesread = psf_sndReadFloatFrames(ifd,frame,1);
while (framesread == 1){
/* フレームデータ生成の処理 */
  psf_sndWriteFloatFrames(ofd,frame,1);
  framesread = psf_sndReadFloatFrames(ifd,frame,1);
}