WAVEファイルの波形をgnuplotで表示してみる
サウンドエフェクト、信号処理に興味があるが前提知識が全く無い状態。
とっかかりとして、まずはWAVEファイルを理解することから初めてみることにした。
今回はかじり得た知識を使ってWAVEファイルから音データを抜き出して波形を表示してみた。実行環境はLinux(CentOS 7)。
[rtoc_mokuji title=”” title_display=”” heading=”h3″ list_h2_type=”” list_h3_type=”” display=”” frame_design=”” animation=””]
目次
WAVEファイルとは
WAVEファイルとはWindowsで音情報をディジタル信号に変換したデータのこと。例えばWindowsの警告音やシャットダウン時の音で使用されている。
波形表示対象のWAVEファイル
新しく用意するのも面倒なので下記のWindows PCのエラー音(Windows エラー.wav)を使うことにした。

ファイルフォーマット
公式な文書は見つけらなかったので、下記を参考にさせていただいた。
ファイルフォーマットと照らし合わせながら実際のパラメータ値がどうなっているか確認する。
#
# Windows エラー.wavはLinuxに転送してWindows_Error.wavにリネームしている
#
[user@localhost wav]$ hexdump -C Windows_Error.wav |head
00000000 52 49 46 46 24 9e 02 00 57 41 56 45 66 6d 74 20 |RIFF$...WAVEfmt |
00000010 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00 |........D.......|
00000020 04 00 10 00 64 61 74 61 00 9e 02 00 00 00 00 00 |....data........|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000006a0 00 00 00 00 00 00 00 00 00 00 ff ff 00 00 fe ff |................|
000006b0 00 00 fc ff 00 00 fa ff 00 00 fd ff 00 00 09 00 |................|
000006c0 00 00 26 00 00 00 4f 00 00 00 82 00 00 00 bb 00 |..&...O.........|
000006d0 00 00 f6 00 00 00 2e 01 00 00 65 01 00 00 95 01 |..........e.....|
000006e0 00 00 bd 01 00 00 db 01 00 00 e9 01 00 00 e4 01 |................|
RIFFチャンク
パラメータ | サイズ(byte) | 値(16進数) | 補足 |
---|---|---|---|
チャンク識別子 | 4 | 0x52494646 | ‘RIFF’ |
チャンクサイズ | 4 | 0x00029e24 | 171556byte ヘッダサイズ(36byte)+dataチャンクのチャンクサイズ |
フォーマットタイプ | 4 | 0x57415645 | ‘WAVE’ |
fmtチャンク
パラメータ | サイズ(byte) | 値(16進数) | 補足 |
---|---|---|---|
チャンク識別子 | 4 | 0x666D7420 | ‘fmt’ |
チャンクサイズ | 4 | 0x00000010 | 16bit |
フォーマットタイプ | 2 | 0x0001 | PCM |
チャンネル数 | 2 | 0x0002 | ステレオ |
サンプリング周波数 | 4 | 0x0000ac44 | 44.1Khz |
1秒あたりバイト数 | 4 | 0x0002b110 | サンプリング周波数×ブロックサイズ |
ブロックサイズ | 2 | 0x0004 | 4byte |
量子化精度 | 2 | 0x0010 | 16bit |
dataチャンク
パラメータ | サイズ(byte) | 値(16進数) | 補足 |
---|---|---|---|
チャンク識別子 | 4 | 0x64617461 | ‘data’ |
チャンクサイズ | 4 | 0x00029e00 | 171520byte |
16bitステレオなのでデータを抽出するときは4byte(L,Rの順で2byte)ずつ読み込んでいけば良いようだ。
データ抽出プログラム
構造体は下記のように定義しておく。
/* RIFFチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('RIFF'固定) */
uint32_t chunk_size; /* チャンクサイズ */
char format_type[4]; /* フォーマットタイプ('WAVE'固定) */
} RIFF_chunk_t;
/* fmt チャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('fmt '固定) */
uint32_t chunk_size; /* チャンクサイズ */
uint16_t format_type; /* フォーマットタイプ */
uint16_t channel; /* チャンネル数 */
uint32_t sample_per_sec; /* サンプリング周波数 */
uint32_t byte_per_sec; /* 1秒あたりバイト数 */
uint16_t block_size; /* ブロックサイズ */
uint16_t bit_per_sample; /* 量子化精度 */
} fmt_chunk_t;
/* dataチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('data')固定 */
uint32_t chunk_size; /* チャンクサイズ(データ長) */
uint8_t dat[0]; /* データ(可変長) */
} data_chunk_t;
typedef struct {
RIFF_chunk_t riff;
fmt_chunk_t fmt;
data_chunk_t data;
} wave_format_t;
typedef struct {
int16_t left;
int16_t right;
} wave_stereo_t;
読み込んだデータは3列(時間 L(左) R(右))で表示する(後でgnuplotに食わせる為)。
static void printWaveData(wave_format_t* wave)
{
int t = 0;
for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
/* time left right */
wave_stereo_t* val =
(wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];
printf("%-08d %-05d %-05d\n", t, val->left, val->right);
}
}
ソースコード全体(plotwav.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
/* RIFFチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('RIFF'固定) */
uint32_t chunk_size; /* チャンクサイズ */
char format_type[4]; /* フォーマットタイプ('WAVE'固定) */
} RIFF_chunk_t;
/* fmt チャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('fmt '固定) */
uint32_t chunk_size; /* チャンクサイズ */
uint16_t format_type; /* フォーマットタイプ */
uint16_t channel; /* チャンネル数 */
uint32_t sample_per_sec; /* サンプリング周波数 */
uint32_t byte_per_sec; /* 1秒あたりバイト数 */
uint16_t block_size; /* ブロックサイズ */
uint16_t bit_per_sample; /* 量子化精度 */
} fmt_chunk_t;
/* dataチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('data')固定 */
uint32_t chunk_size; /* チャンクサイズ(データ長) */
uint8_t dat[0]; /* データ(可変長) */
} data_chunk_t;
typedef struct {
RIFF_chunk_t riff;
fmt_chunk_t fmt;
data_chunk_t data;
} wave_format_t;
typedef struct {
int16_t left;
int16_t right;
} wave_stereo_t;
static wave_format_t* getWaveData(char* path);
static void delWaveData(wave_format_t* wave);
static void printWaveData(wave_format_t* wave);
int main(int argc, char*argv[])
{
if (argc != 2) {
fprintf(stderr, "usage:%s <wave file>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (access(argv[1], R_OK) != 0) {
perror("access");
exit(EXIT_FAILURE);
}
wave_format_t *wave = getWaveData(argv[1]);
if (NULL == wave) {
fprintf(stderr, "getWaveData(%s) failed.\n", argv[1]);
exit(EXIT_FAILURE);
}
printWaveData(wave);
delWaveData(wave);
return 0;
}
static wave_format_t* getWaveData(char* path)
{
FILE* fp = NULL;
char buff[sizeof(wave_format_t)] = {0};
wave_format_t* wave = NULL;
fp = fopen(path, "rb");
if (NULL == fp ) {
goto error_end;
}
if (fread(buff, 1, sizeof(buff), fp) <= 0) {
goto error_close_end;
}
wave = (wave_format_t* )malloc(((wave_format_t* )buff)->riff.chunk_size);
if (NULL == wave) {
goto error_close_end;
}
*wave = *((wave_format_t* )buff);
fread(wave->data.dat, 1, wave->data.chunk_size, fp);
error_close_end:
fclose(fp);
error_end:
return wave;
}
static void delWaveData(wave_format_t* wave)
{
if (NULL != wave) {
free(wave);
}
}
static void printWaveData(wave_format_t* wave)
{
int t = 0;
for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
/* time left right */
wave_stereo_t* val =
(wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];
printf("%-08d %-05d %-05d\n", t, val->left, val->right);
}
}
波形表示
[user@localhost wav]$ gcc -o plotwav plotwav.c
[user@localhost wav]$ ./plotwav Windows_Error.wav > wave.plt
[user@localhost wav]$ gnuplot
G N U P L O T
Version 4.6 patchlevel 2 last modified 2013-03-14
Build System: Linux x86_64
Copyright (C) 1986-1993, 1998, 2004, 2007-2013
Thomas Williams, Colin Kelley and many others
gnuplot home: http://www.gnuplot.info
faq, bugs, etc: type "help FAQ"
immediate help: type "help" (plot window: hit 'h')
Terminal type set to 'x11'
gnuplot> plot "wave.plt" using 1:2 with lines, "wave.plt" using 1:3 with lines

コメント