見出し画像

音声信号処理、wavファイル波形表示

音声信号処理の勉強の為、書籍等を参考にして、実際にソフトウエア等を作成し実験してみた。今回は、wavファイルを読み込んで音声信号波形データを表示させる。
参考にさせていただいた書籍は、
サウンドエフェクトのプログラミング Cによる音の加工と音源合成
以前Free C++ Compiler (BCC102)をインストールする為、以下のWEBページ
実践編・Free C++ Compiler 開発環境ポータブル化
を参考にさせていただいていたが、この中でGLUTを使う為セットアップ方法も書かれていて自分も参考にしてFree C++ Compiler (BCC102)でGLUTが使用出来る様にセットアップしていた。
OpenCV等の他のライブラリーを使用する方が準備に手間が掛かる為、今回はwavファイルの波形をGLUTでグラフィック表示させる事にした。
GLUTの使い方は以下のWEBページを参考にさせていただいた。
グラフィックスの基礎

書籍「サウンドエフェクトのプログラミング Cによる音の加工と音源合成」のP53~P55の「3.7 正弦波音ファイルの作成」のサンプルプログラムを試していた。
念の為、「3.7 正弦波音ファイルの作成」のサンプルプログラムは以下の様にしてコンパイルした。
bcc32c -c wavwrite.c
bcc32c sine.c wavwrite.obj
実行例として、
・例1 44,100サンプル(1秒間), 最大値16,383(0x3FFF半分), 音声周波数1kHz, サンプリングレート44.1kHz, ファイル名test1000hz.wav
sine 44100 16383 1000 44100 test1000hz.wav
ファイル作成OK
・例2 念の為これは試さないで下さい。
44,100サンプル(1秒間), 最大値65,535(0xFFFF2倍), 音声周波数1kHz, サンプリングレート44.1kHz, ファイル名test1000hz_x2.wav
sine 44100 65535 1000 44100 test1000hz_x2.wav
ファイルが出来ないで終了した。ハングしたかも。(16bit符号付き整数の範囲を超えている為、再生してはいけない不定な数値のデータになるとは思ったが、、、)
・例3 44,100サンプル(1秒間), 最大値32,767(0x7FFFフル), 音声周波数1kHz, サンプリングレート44.1kHz, ファイル名test1000hz_full.wav
sine 44100 32767 1000 44100 test1000hz_full.wav
ファイル作成OK

当初、出来た波形の確認は、Sazanamiを使用させていただいていたが、勉強の為にも自分で表示プログラムを作ってみる事にした。
作成したプログラムは、wav_data_disp.c
今回も音声処理の為、書籍「サウンドエフェクトのプログラミング Cによる音の加工と音源合成」のwavlib.hとwavread.cを使用させていただいた。
コンパイル方法は、
bcc32c -c wavread.c
bcc32c wav_data_disp.c wavread.obj glut32.lib

画像1 コンパイルしているところ

実行方法は、
wav_data_disp
実行後、表示するwavファイル名を入力する。
test1000hz.wav

画像2 実行し表示するwavファイル名を入力する

以下の様に波形が表示される。

画像3 実行結果1

test1000hz_full.wavの波形も表示させた。

画像4 実行してtest1000hz_full.wavを指定する
画像5 実行結果test1000hz_full.wavを表示

グラフィック表示の横の目盛りは、上から100%, 50%, 0%, -50%, -100%で
test1000hz.wavが半分程のふり幅である事と、
test1000hz_full.wavがふり幅が最大値から最小値まである事が確認出来た。

以下にプログラムwav_data_disp.cを載せる。
また、今回も書籍のwavlib.hとwavread.cをそのまま使わせていただいている為、こちらは載せるのを控えています。
16bitデータ、1chの表示に機能が絞られています。

// wavファイルのdataを波形表示させる
#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>  // GLUT
#include "wavlib.h"   // wavライブラリー使用手続き

int WINDOW_WIDTH = 500;   // ウィンドウの横幅
int WINDOW_HEIGHT = 500;  // ウィンドウの高さ

wav_head bufhead;         // wavファイルヘッダー部分格納

void init()
{
  glClearColor(1.0, 1.0, 1.0, 1.0);     // r, g, b, 不透明白
}

void display()
{
  int i;
  int x_step = 2;
  //int x_max = 499 - 2;
  int x_lim;
  int wav_yd;
  int wav_yd_ofset = 250;
  int wav_yd_n1 = 0;  // 前回のy座標
  short *ibuf;
  ibuf   = bufhead.data;
  // ウィンドウ表示内容のクリア
  glClear(GL_COLOR_BUFFER_BIT);

  // 目盛り線+100%
  glColor3d(0.5, 0.5, 0.5);  // グレー
  glBegin(GL_LINES);
  glVertex2d(0, 450);
  glVertex2d(WINDOW_WIDTH - 1, 450);
  glEnd();
  // 目盛り線+50%
  glColor3d(0.5, 0.5, 0.5);  // グレー
  glBegin(GL_LINES);
  glVertex2d(0, 350);
  glVertex2d(WINDOW_WIDTH - 1, 350);
  glEnd();
  // 目盛り線0%
  glColor3d(0.5, 0.5, 0.5);  // グレー
  glBegin(GL_LINES);
  glVertex2d(0, 250);
  glVertex2d(WINDOW_WIDTH - 1, 250);
  glEnd();
  // 目盛り線-50%
  glColor3d(0.5, 0.5, 0.5);  // グレー
  glBegin(GL_LINES);
  glVertex2d(0, 150);
  glVertex2d(WINDOW_WIDTH - 1, 150);
  glEnd();
  // 目盛り線-100%
  glColor3d(0.5, 0.5, 0.5);  // グレー
  glBegin(GL_LINES);
  glVertex2d(0, 50);
  glVertex2d(WINDOW_WIDTH - 1, 50);
  glEnd();

  // 波形描画
  x_lim = (WINDOW_WIDTH - 3) / x_step;
  if (x_lim > (bufhead.Nbyte / bufhead.bl_size))  x_lim = bufhead.Nbyte / bufhead.bl_size;
  for (i = 0; i < x_lim; i++) {
    // 今回のy座標計算
    wav_yd = (int)((double)ibuf[i] * (400.0 / 65535.0));
    if (wav_yd < -200)  wav_yd = -200;
    else if (wav_yd > 200)  wav_yd = 200;
    // 前回のデータからの縦ライン描画
    glColor3d(0.0, 0.0, 0.0);  // 黒
    glBegin(GL_LINES);
    glVertex2d(i * x_step, wav_yd_n1 + wav_yd_ofset);
    glVertex2d(i * x_step, wav_yd + wav_yd_ofset);
    glEnd();
    // 今回のデータからの横ライン描画
    glColor3d(0.0, 0.0, 0.0);  // 黒
    glBegin(GL_LINES);
    glVertex2d(i * x_step, wav_yd + wav_yd_ofset);
    glVertex2d((i + 1) * x_step, wav_yd + wav_yd_ofset);
    glEnd();
    // 今回のデータ保存
    wav_yd_n1 = wav_yd;
  }

  glFlush();
}

void reshape(int width, int height)
{
  // OpenGLウィンドウの描画範囲を設定
  // 下記は描画範囲が[0, width] x [0, height]となるように設定している
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0.0, (double)width, 0.0, (double)height);
  WINDOW_WIDTH = width;
  WINDOW_HEIGHT = height;
}

// 実行方法: wav_data_disp
// wavファイル名は後から指定
int main(int argc, char** argv)
{
  char *fnI;                            // ファイル名格納
  int i;

  // ファイル名関係
  fnI = calloc(100,sizeof(char));       // メンバー数, 1個のサイズ
  printf("wav file name = ");
  scanf("%s", fnI);                     // キーボード入力 文字列フォーマット, 出力する変数
  // wavファイル読み込み
  wavread(&bufhead, fnI);

  // GLUT
  glutInit(&argc, argv);                            // GLUT 初期化
  glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);  // ウィンドウのサイズを設定
  glutCreateWindow(fnI);                            // ウィンドウの作成 (引数はウィンドウのタイトル)
  glutDisplayFunc(display);                         // 描画に使う関数の登録
  glutReshapeFunc(reshape);                         // ウィンドウのサイズ変更時に呼ばれる関数の登録
  //glutMouseFunc(mouse);                             // マウス操作時に呼ばれる関数の登録
  init();                                           // OpenGLの初期化処理  (これはコールバック関数ではないので直接呼び出す)

  glutMainLoop();                                   // 描画ループの開始
}

今回は以上です。

この記事が気に入ったらサポートをしてみませんか?