LoginSignup
24
21

More than 5 years have passed since last update.

WAVEファイル読み・書き込み

Last updated at Posted at 2016-01-04

WAVEファイルの読み込みと書き込みをC言語を用いて行う。
参考文献:C言語で始める音のプログラミング

WAVEファイルについて

フォーマット
alt

WAVEファイルはRIFF(Resource Interchange File Format)チャンクと呼ばれるブロック構造により、音のデータを記録している。

RIFFチャンクの中にfmtチャンクとdataチャンクが含まれている。

  • fmtチャンク:標本化周波数や量子化精度など音データのプロパティを格納
  • dataチャンク:音データそのもの

プログラム

400Hzの波形を出力する音声ファイルを作成し、それを読み込むプログラム

lang:main.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "wave.h"

int main(int argv, char **argc)
{
    MONO_PCM pcmForWrite, pcmForRead;
    int i;
    double amplitude, frequency;

    pcmForWrite.fs = 8000;
    pcmForWrite.bits = 16;
    pcmForWrite.length = 8000;
    pcmForWrite.s = calloc(pcmForWrite.length, sizeof(double));

    amplitude = 0.25;
    frequency = 400.0;

    for(i = 0; i < pcmForWrite.length; i++)
    {
        pcmForWrite.s[i] = amplitude * sin((2.0 * M_PI * frequency * i) / pcmForWrite.fs);
        // A sin(2πft0/fs) + A/2 sine(2πft1/fs) ・・・
    }
    monoWaveWrite(&pcmForWrite, "testWave.wav");
    free(pcmForWrite.s);

    monoWaveRead(&pcmForRead, "testWave.wav");
    for(i = 0; i < pcmForRead.length; i++)
    {
        printf("%d:%lf\n", i, pcmForRead.s[i]);
    }
    free(pcmForRead.s);

    return 0;
}
lang:wave.h
#ifndef WAVE_H
#define WAVE_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define GROUND 32768.0
#define MAXPLUS 65536.0
#define MAXMINUS 0.0

typedef struct
{
    int fs;
    // 標本化周波数
    int bits;
    // 量子化制度
    int length;
    // データ長
    double *s;
    // 音データ
} MONO_PCM;
// モノラルの音声データの定義

typedef struct
{
    char chunkID[4];
    long chunkSize;
    char chunkFormType[4];
} RIFF_CHUNK;
// RIFFチャンクの定義

typedef struct
{
    char chunkID[4];
    long chunkSize;
    short waveFormatType;
    short formatChannel;
    long samplesPerSec;
    long bytesPerSec;
    short blockSize;
    short bitsPerSample;
} FMT_CHUNK;
// fmtチャンクの定義

typedef struct
{
    char chunkID[4];
    long chunkSize;
    short data;
} DATA_CHUNK;
// dataチャンクの定義

typedef struct
{
    RIFF_CHUNK riffChunk;
    FMT_CHUNK fmtChunk;
    DATA_CHUNK dataChunk;
} WAVE_FORMAT;
// WAVEフォーマット

void monoWaveRead(MONO_PCM *pcm, char *fileName);
// モノラルの音声データ(waveファイル)の読み込み

void monoWaveWrite(MONO_PCM *pcm, char *fileName);
// モノラルの音声データ(waveファイル)書き込み

#endif
wave.c
#include "wave.h"

void monoWaveRead(MONO_PCM *pcm, char *fileName)
{
    FILE *fp;
    int i;
    WAVE_FORMAT waveFormat;

    fp = fopen(fileName, "rb");

    if(!fp)
    {
        printf("file open error");
        exit(0);
    }

    fread(waveFormat.riffChunk.chunkID, 1, 4, fp);
    fread(&waveFormat.riffChunk.chunkSize, 4, 1, fp);
    fread(waveFormat.riffChunk.chunkFormType, 1, 4, fp);
    // RIFFチャンクの読み込み

    fread(waveFormat.fmtChunk.chunkID, 1, 4, fp);
    fread(&waveFormat.fmtChunk.chunkSize, 4, 1, fp);
    fread(&waveFormat.fmtChunk.waveFormatType, 2, 1, fp);
    fread(&waveFormat.fmtChunk.formatChannel, 2, 1, fp);
    fread(&waveFormat.fmtChunk.samplesPerSec, 4, 1, fp);
    fread(&waveFormat.fmtChunk.bytesPerSec, 4, 1, fp);
    fread(&waveFormat.fmtChunk.blockSize, 2, 1, fp);
    fread(&waveFormat.fmtChunk.bitsPerSample, 2, 1, fp);
    // fmtチャンクの読み込み

    fread(waveFormat.dataChunk.chunkID, 1, 4, fp);
    fread(&waveFormat.dataChunk.chunkSize, 4, 1, fp);

    pcm->fs = waveFormat.fmtChunk.samplesPerSec;
    pcm->bits = waveFormat.fmtChunk.bitsPerSample;
    pcm->length = waveFormat.dataChunk.chunkSize;
    pcm->s = calloc(pcm->length, sizeof(double));

    short data;
    for(i = 0; i < pcm->length; i++)
    {
        fread(&data, 2, 1, fp);
        pcm->s[i] = (double)data/GROUND;
    }
    // dataチャンクの読み込み

    fclose(fp);
}

void monoWaveWrite(MONO_PCM *pcm, char *fileName)
{
    FILE *fp;
    int i;
    WAVE_FORMAT waveFormat;

    strcpy(waveFormat.riffChunk.chunkID, "RIFF");
    waveFormat.riffChunk.chunkSize = 36 + pcm->length * 2;
    strcpy(waveFormat.riffChunk.chunkFormType, "WAVE");

    strcpy(waveFormat.fmtChunk.chunkID, "fmt ");
    waveFormat.fmtChunk.chunkSize = 16;
    waveFormat.fmtChunk.waveFormatType = 1;
    // PCMの場合は1
    waveFormat.fmtChunk.formatChannel = 1;
    // モノラルの場合は1,ステレオの場合は2
    waveFormat.fmtChunk.samplesPerSec = pcm->fs;
    waveFormat.fmtChunk.bytesPerSec = (pcm->fs * pcm->bits) / 8;
    waveFormat.fmtChunk.blockSize = pcm->bits / 8;
    waveFormat.fmtChunk.bitsPerSample = pcm->bits;

    strcpy(waveFormat.dataChunk.chunkID, "data");
    waveFormat.dataChunk.chunkSize = pcm->length * 2;

    fp = fopen(fileName, "wb");

    if(!fp)
    {
        printf("file open error");
        exit(0);
    }

    fwrite(waveFormat.riffChunk.chunkID, 1, 4, fp);
    fwrite(&waveFormat.riffChunk.chunkSize, 4, 1, fp);
    fwrite(waveFormat.riffChunk.chunkFormType, 1, 4, fp);
    // RIFFチャンクの書き込み

    fwrite(waveFormat.fmtChunk.chunkID, 1, 4, fp);
    fwrite(&waveFormat.fmtChunk.chunkSize, 4, 1, fp);
    fwrite(&waveFormat.fmtChunk.waveFormatType, 2, 1, fp);
    fwrite(&waveFormat.fmtChunk.formatChannel, 2, 1, fp);
    fwrite(&waveFormat.fmtChunk.samplesPerSec, 4, 1, fp);
    fwrite(&waveFormat.fmtChunk.bytesPerSec, 4, 1, fp);
    fwrite(&waveFormat.fmtChunk.blockSize, 2, 1, fp);
    fwrite(&waveFormat.fmtChunk.bitsPerSample, 2, 1, fp);
    // fmtチャンクの書き込み

    fwrite(waveFormat.dataChunk.chunkID, 1, 4, fp);
    fwrite(&waveFormat.dataChunk.chunkSize, 4, 1, fp);

    short data;
    double s;
    for(i = 0; i < pcm->length; i++)
    {
        s = ((pcm->s[i] + 1.0) / 2) * (MAXPLUS + 1.0);
        if(s > MAXPLUS)
            s = MAXPLUS;
        else if(s < MAXMINUS)
            s = MAXMINUS;

        data = (short)(s + 0.5) - GROUND;
        fwrite(&data, 2, 1, fp);
    }

    fclose(fp);
}

まとめ

  • fmtチャンクのchunkIDを書き込む箇所でstrcpyを使っている。strcpyはコピー元の文字列のサイズがコピー先のサイズに満たない場合「¥0」が追記される。これによりWAVEファイルとして認識されず、音が鳴らなかった。
  • 次回は様々な波形をプログラムにより作成してみる。
24
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
21