Sound Analysis - WAV header & Gemerate WAV file(c/c++)

WAV 구조와 간단한 소리 만들기(C/C++)
우선, 가장 쉬운 Wave(Waveform Audio File)를 맛보기로 진행해본다. Wave는 무압축 포맷으로 있는 그대로의 숫자들을 소리를 구성하는 값이기 때문에 접근하기 훨씬 편하다. 물론, 파일을 구분하기 위한 헤더가 필요하다. 헤더 또한 매우 간단하게 구성되어있다.

Chunks of WAV

RIFF
Chunk Size4Byte
Format4Byte
FMT
Chunk ID4Byte
Chunk Size4Byte
Audio Format2Byte
Number of Channels2Byte
Sample rate4Byte
Byte rate4Byte
Block align2Byte
Bit Per Sample2Byte
DATA
Chunk ID4Byte
Chunk Size4Byte
Data?Byte

보시다시피 3개의 Chunk(덩어리)으로 이루어져있다. 공통적으로 각 Chunk 마다 Chunk ID와 Chunk Size는 공통으로 Chunk에 대한 고유 ID와 Size를 갖고 있다. 그 외에는 각각 들어가야 할 값들이 있다. 아래 설명을 보고 그에 맞는 값을 넣는다.

RIFF

  • Chunk ID(4) : 'RIFF' 라는 문자가 ASCII으로 입력된다. wave 파일의 규칙이다.
  • Chunk Size(4) : 전체 파일의 크기를 기재한다. 아래 설명을 본 후에 다시 이해해야 한다. 소리의 길이 x Sample rate x Header size(36Byte)를 넣는다.
  • Format(4) : 파일 형식이다. 'WAVE'

FMT

  • Chunk ID(4) : 'fmt '(마지막에 공백이 포함)으로 고정값이다.
  • Chunk Size(4) : 아래 항목들의 사이즈 합인 16Byte(0x10)
  • Audio Format(2) : PCM 데이터를 나타내는 '1'
  • Number of Channels(2) : 말 그대로 채널 수의 개수를 나타낸다. mono, stereo 등을 말한다.
    • 1 / mono / [data][data][data]...
    • 2 / stereo / [left][right][left][right]...
    • 3 / 3 channel / [left][right][center][left][right][center]...
    • 4 / quad / [front left][front right][rear left][rear right]...
    • 5 / 4 channel / [left][center][right][surround]...
    • 6 / 6 channel / [left center][left][center][right center][right][surround]...
    • Sample rate(4) : 우리가 쉽게 접하는 Hz. 1초의 소리가 몇 개의 데이터로 구성되어있는가를 나타낸다. 우리가 가장 많이 사용하는 Sample rate는 44100Hz이다.
    • Byte rate(4) : 1초 동안 소리를 내는데 필요한 Byte수를 말한다. 1Channel(mono)이면 Sample rate와 같을 것이고, 주로 우리가 접하는 Stereo는 Sample rate x 2 와 같다.
    • Block align(2) : 샘플 하나의 개수를 말한다. (Bit Per Sample/8) 이 들어간다.
    • Bit Per Sample(2) :하나의 샘플을 구성하는 비트의 개수를 입력한다.

Data

  • Chunk ID(4) : 'data'를 입력한다.
  • Chunk Size(4) : 헤더의 크기를 제외한 실제 데이터의 파일 데이터이다,
  • Data (?) : 실제 소리를 만드는 값들이 들어간다.

위 헤더 모양과 같이 각각에 값을 넣어주고 Data 부분에 실제 연속된 신호값을 넣으면 완성된다. 실제 신호를 이루는 값들은 어떻게 만드는가? 모든 소리는 아래 수식이 기반이 된다.

Circle cos sin.gif
"Circle cos sin" by LucasVB - Own work. Licensed under Public domain via Wikimedia Commons.

//
//  main.cpp
//  SoundAnalaysis
//
//  Created by Seonwoon Kim on 13. 10. 16..
//  Copyright (c) 2013년 Seonwoon Kim. All rights reserved.
//

#include 
#include 
#include 
#include 

#define PI 3.14159265
#define WAV_HEAD_SIZE 44
#define FS  44100
#define SEC 3

typedef struct{
    // RIFF
unsigned char riff[4];
unsigned int len;
unsigned char wave[4];
    // FMT
unsigned char fmt[4];
unsigned int flen;
unsigned short one;
unsigned short chan;
unsigned int hz;
unsigned int bpsec;
unsigned short bpsmp;
unsigned short bitpsmp;
    // DATA
unsigned char dat[4];
unsigned int dlen;
}WAVHDR;

void createWavHeader(unsigned char *m, unsigned int hz, unsigned int dlen);
int main(int argc, const char * argv[]){
unsigned char header[WAV_HEAD_SIZE];
unsigned char signal[FS*SEC];

float freq = 600.0f;
unsigned char amp = 350;

int i;

FILE* file = fopen("out.wav", "w");

createWavHeader(header, FS, (FS*SEC));
fwrite(header, 1, WAV_HEAD_SIZE, file);

for(i = 0; i < (FS*SEC); i++){
signal[i] = sin(i*((PI*2/FS)*freq))*amp+128;
}
fwrite(signal, 1, FS*SEC, file);
fclose(file);
return 0;
}

void createWavHeader(unsigned char *m, unsigned int hz, unsigned int dlen){
WAVHDR *p = (WAVHDR*)m;
    memcpy(p->riff, "RIFF", sizeof(p->riff));
p->len    = dlen+WAV_HEAD_SIZE;
    memcpy(p->wave, "WAVE", sizeof(p->wave));
    memcpy(p->fmt, "fmt ", sizeof(p->fmt));
p->flen  = 0x10;
p->one    = 1;
p->chan  = 1;
p->hz    = hz;
p->bpsec  = hz;
p->bpsmp  = 1;
p->bitpsmp= 8;
    memcpy(p->dat, "data", sizeof(p->dat));
p->dlen  = dlen;
}