音遊び日記

ハードウェアとソフトウェアの両面から”音”で遊んだ事を備忘録として書いています。

波形テーブルの作成~wavからcsvファイルに変換する~

1. はじめに

 マイコンなどで正弦波や方形波以外の複雑な音声を出力しようとした時、最もシンプルな方法として波形テーブルがあります。波形テーブルとは波形を定数の配列としてソースコード上に宣言し、それを順番に出力する事で音声を出力する方法です。図で表すとこんな感じ。

f:id:hsy221:20191023160003p:plain

 今回はC言語を使って簡単にwav音源ファイルからcsv数値列ファイルを作成する方法を紹介します。これでDTMプラグイン音源とか、自分で収録した楽器の音源ファイルから簡単に波形テーブルを作成できます。

 

2. ソースコード

 以前本ブログで紹介した、C言語でwavファイルを読み込む関数を使います。詳細はこちら↓

hwswsgps.hatenablog.com

 

 wavファイルを読み込む関数 audio_readを使ってwavファイルをヘッダ部分と音声データ部分に分けて変数に代入します。まずは宣言やらコマンドライン引数やらの準備部分とwavファイルの読み込み部分までのソースコードです。

#include <stdio.h>
#include <stdlib.h>
#include "audioio.h"

int main(int argcchar *argv[])
{
  //変数宣言
  FILE *fp;
  WAV_PRM prm_in;
  double *data_in;
  unsigned short wdata;
  int n;
  char filename[64];
  
  //コマンドライン引数が違う場合
  if(argc != 2){
    printf("引数が違います\n");
    exit1 );
  }
  
  //wavファイルの読み込み
  data_in = audio_read(&prm_in, argv[1]);

wavファイルの読み込みが完了したら、コマンドライン上で出力ファイル名(.csv)を入力させ、そのファイルをfopenで開きます。ここで、すでに同名のファイルがある場合は上書きされ、無い場合は新規作成されます。fopenはこのプログラム上でそのファイルを使えるようにするために行います。

  //出力ファイル名入力
  printf("output file name : ");
  scanf("%s", filename);

  //出力用ファイルオープン
  fp = fopen(filename, "w");
  if(fp == NULL){
    printf("%sが開けません\n", filename);
    return -1;
  }

最後に読み込んだデータを名前を先ほど入力したファイルに書き込みます。audio_readで読み込んだ値は-1~1で正規化されており、今回は0~65535の16bitで表現したかったので、変換しています。ここら辺は用途によって変わってくると思います。また記述もちゃんと型変換を行わないといけないと思うのですが、そこらへんちゃんと把握していないので無理やり代入してます(勉強しなくては、、、)。

  //データ書き込み
  for (n = 0;n < prm_in.L;n++){
    wdata = ((data_in[n] + 1* 65535 / 2);
    fprintf(fp, "%d,", wdata);
  //fprintf(fp, "%f", data_in[n])  ←-1~1の正規化データをそのまま使う場合
  }

  //メモリ解放
  free(data_in);

  return 0;

audio_read関数が便利なのと波形切り出したり、長さ調整とかをしていないので、かなり短いソースコードで済みました。このシンプルなコードではwavファイル上で正確なサンプル数で作りこまないといけません。

 打楽器音源をリズムマシンとして使う場合や単発の効果音などであれば問題ありませんが、弦楽器や管楽器など連続波形の一波形の波形テーブルを作りたい場合、正確なサンプル数で1波長分のwavファイルを作成しなければなりません。それに対する考察を次章以降で行います。

 

3. 1波長分の波形テーブルのサンプル数について

 この章の話は実装して検証したわけでは無いので、想像として聞いてください。。。

 

 プログラムの記述を考えれば波形テーブルのサンプル数は少ないに越したことはありません。ところがサンプル数が少なければその分、元の波形の再現度が低くなり、音質低下につながります。

 仮に8bit(256sample)で1波長を表現した場合、サンプリング周波数44.1kHzで再生すると、44.1k / 256 = 172Hzより高い周波数ではサンプルの間引きが行われ、それ未満ではサンプルが足りません。低周波ほど波長が長くなるため、たくさんのサンプルを必要とします。低周波もきちんと再生したい場合もっとサンプル数を増やす必要があると考える事が出来ます。

 サンプリング周波数44.1kHzで86Hzの音は1波長当たり512サンプルで表現されますが、波形テーブルは256サンプルの時、足りないサンプルは同じサンプル二回呼び出すことになりますがこれでいいと思います。なぜかと言うと様々なサンプル補完デジタルアルゴリズムがありますが、DA変換時のLPFでサンプル間は補完されるため、この規模であればデジタル補完はいらないと思うからです。

 また86Hzの音は半分の22.05kHzでサンプリングすれば1波長当たり256サンプルとなりますが、果たして86Hzの音に対してサンプリング周波数を半分にした所で、音質に違和感はないと思います。演算のしやすさから8bitで十分(ちょっと多いくらい)だと思っています。

 

4.課題

 さて本題の波形テーブルの作成についてですが、打楽器や単発の効果音の波形テーブルを作りたい場合は、音源から目的の音をsound engineとかで切り取ってさっきのプログラムに入れればいいですが、弦楽器や管楽器などで一波長だけ切り出して連続波形として使用したい場合、さっきのプログラムはwavデータすべてをcsvファイルに変換するため、一波長のみの音源ファイルを作る必要が出てきます。

 それはサンプリング周波数に対して正確な周波数の信号を鳴らした音源が必要となります。DTMで打ち込みで音源を作るなら可能かもしれませんが、生の音を収録してそれを使いたい場合は正確な周波数で鳴らすなど不可能です。

 今後はプログラムを改良して、自動で音源から一波長分のデータのみを抽出するように(気が向いたら)しようと思います。