소리 (sound) 는사람의음성 (speech), 악기소리, 자연음등을포함한포괄하는개념이다. 소리매체를컴퓨터로처리할수있게하기위해서는소리의발생형태와인간이소리매체를인지하는방식에대한이해가먼저요구된다. 본장에서는이러한소리매체의개념과특성에대한기본적인지식과스펙트럼에대해소개한다. 1.1 소리의발생원리소리란어떤물질이진동함으로써발생하는물리적인현상이다. 물질이진동하면그주위를둘러싸고있는공기중의압력이변화하며, 이로인해고압과저압이교대로생겨공기중으로물결과도같이전파하는음파를발생시킨다. 이러한파동이인간의귀에닿으면청각신경을통하여들리게된다. 1.2 소리신호의구분소리파형 (sound waveform) 은그림 1.1 과같이주기적인파형으로나타낼수있다. 파형은규칙적인간격마다같은모양을반복하는데, 이때같은모양이한번나타나는데소요되는시간을주기 (period) 라고한다. 일반적인자연현상에의해발생하는소리파형은완전히주기적인모양을갖지않는다. 그러나, 사람의음성중에모음, 악기에서발생하는소리, 새소리, 바람소리등은주기적인형태를보이며, 사람음성의자음, 물흐르는소리, 재채기, 자동차정차소리등과같은음향신호는비주기적인형태를갖게된다. 소리신호의주기에대한역수즉, 1 초당주기의반복되는횟수를주파수 (frequency) 라고하고, 단위는헤르쯔 (Hertz, Hz=1/sec) 를사용한다. 소리신호에대한주파수대역은대략적으로표 1.1 과같이구분할수있다. 소리신호의구분은각종응용분야에따라적절하게이용된다. - 1 -
진폭 A 시간 -A 주기 (T) 1/f1 (a) 정현파 진폭 A 시간 -A 주기 (T) 1/f1 (b) 구형파 그림 1.1 주기적인신호의파형에대한예제 표 1.1 소리신호의주파수영역구분 주파수대역범위 ( 대역폭 ) 초저음파 (infra-sound) 가청주파수 (human hearing frequency range) 초음파 (ultrasound) 극초음파 (hypersound) 인간의음성 (voice/speech) 0 ~ 20 Hz 20Hz ~ 20kHz 20kHz ~ 1GHz 1GHz ~ 10THz 0 ~ 3.5kHz - 2 -
인간의가청주파수영역에서발생되는신호를오디오 (audio) 라고하며, 이주파수대역은컴퓨터의사운드카드및스피커음향등귀로청취하는멀티미디어장치설계에밀접하게관계한다. 초음파의경우에는간질환, 태아검사등등의료기기에사용되어질병을진단하는데쓰이고있다. 박쥐의경우에는인간이청취할수없는초음파를이용하여서로간에의사소통을한다. 반면에, 사람의입으로발생시킬수있는소리의주파수대역은귀로들을수있는영역보다매우좁으며, 마이크로폰, 사운드카드, 휴대폰등의음성처리분야에널리이용되고있다. 음성 (speech) 이라하면소리중에정상적인상태에서인간이성대를통하여발생시키는소리라고할수있으며, 특별한의미를가지지않는감탄사, 고성까지의인간이발생시키는목소리 (voice) 는음성연구분야보다다소확장된의미의소리라고할수있다. 1.3 소리신호의강도소리신호의크기는그림 1.1 에서진폭 (amplitude) 에비례한다. 일반적으로소리신호의크기는세기 (loudness) 와강도 (intensity) 로나누어다룬다. 소리신호를듣는주체가심리적으로느끼는소리의크기를소리의세기라하고, 이것은주관적인소리크기를말한다. 즉, 낮과밤에듣는동일소리의크기, 시끄러운장소에서의소음등은듣는주체가시간과장소에따라느끼는소리의크기는다르다. 이와달리, 인간이느끼는심리적인요인을완전히배제한물리적인소리의크기를소리의강도라고한다. 소리의강도를표현하는데널리쓰이고있는기준단위로는데시벨 (decibel: db) 이있다. 데시벨은특정기준이되는소리강도를미리정하고그기준에비해측정소리의크기를상대적인값으로표현하는측정수치이다. 아래의식은데시벨값을결정해주는식이다. db = 20 log 10 (A/B) 여기서 B 에대입되는기준치소리강도는 2.83x10-4 dyne/cm 2 이다. 위식으로측정하여 100 db ~ 120 db 정도의소리강도에서부터인간은청각신경에고통을느끼게된다고 - 3 -
알려져있으며, 이를고통임계값 (threshold of pain) 이라고한다. 이값은소리매체를표현하고자할때, 디지털음성신호처리를위한소리신호의범위를결정하는데유용하게사용될수있다. 표 1.2 는일반적으로인간들이쉽게접할수있는소리의종류와크기를나타내고있다. 표 1.2 소리의종류와크기에대한예 소리의종류 크기 (db) 소리의종류 크기 (db) 목박는소리 95 속삭이는소리 20 주행하는차도 70 비행기소리 140 공습경보 125 라디오소리 40 1.4 소리신호에대한스펙트럼소리신호의파형은여러개의다양한주파수 (frequency) 에따른진폭 (amplitude) 을갖는파형으로각각분해하여표현할수있다. 이와같이소리신호를주파수와진폭의크기로표현하는것은스펙트럼 (spectrum) 이라한다. 소리신호의스펙트럼은여현함 수 (sine function) 인 a sin( 2πf t) 의중첩된파형으로표현된다. 예로써, 그림 1.2 n n n= 0,1, L 1 1 는연속적신호인 f(t) 를 sin( 2π f1t) + sin(2π (3 f ) 1t) + sin(2π (5 f1) t) 의중첩으로표현할 3 5 수있음을보여주고있다. 이와같이스펙트럼인자로서주파수와진폭을동일한시간 축에대해중첩시키면분해되기이전의원래파형모양에근사하게된다. 이와같은스펙트럼을구하는대표적인방법으로퓨리에변환 (Fourier transform) 을 사용한다. 퓨리에변환은시간축상에서의신호를주파수영역에서의신호로변환하는 것이다. 퓨리에변환에있어서시간축상의신호 F(t) 는주파수상의신호 F(ω ) 로변환 된다. 퓨리에변환은다음과같이정의된다. - 4 -
F [ f ( t) ] = jωt ( ω ) = FT f ( t) e dt ω = 2πf, (1.1) 주파수상으로변환된 F (ω ) 는시간축상에서의신호 f(t) 로구해질수있다. 이를역퓨리에변환 (Inverse Fourier transform) 이라고하며, 다음과같다. f ( t) 1 2π [ F( ω) ] = 1 j = FT f t e ω t ( ) dω 식 (1.1) 에나타낸퓨리에변환의결과는주파수 (1.2) ω = 2πf 의함수로진폭 (amplitude) 과위상 (phase) 성분으로음성신호를표현한다. 식 (1.1) 에서표현되는 F(ω) 는 ω 에 대한복소수형태로표현되기때문에, F ( ω) = a( ω + jb( ω) (1.3) 와같이나타낼수있으며, 이에대한분석은 진폭 : F + 2 2 ( ω) = a( ω) b( ω) b( ω) 위상 : tan 1 ( ) a( ω) (1.4) 와같이수행한다. 이와관련된내용은 5 장에서별도로다루기로한다. 그러나, 실제컴퓨터에서음성신호를다루기위해서는연속적인신호를일정한시간간격으로샘플링된이산신호 (discrete signal) 를처리하므로이산퓨리에변환 (Discrete Fourier Transform) 이사용된다. - 5 -
(a) sin (2πf 1 t) (b) 1/3 sin (2π(3f 1 )t) (c) 1/5 sin (2π(5f 1 )t) (d) sin (2πf 1 t) + 1/3 sin (2π(3f 1 )t) + 1/5 sin (2π(5f 1 )t) 1 1 그림 1.2 f(t)= sin( 2π f1t) + sin(2π (3 f ) 1t) + sin(2π (5 f1) t) 에대한표현 3 5 이상에서다룬퓨리에변환을통하여소리신호의파형을여러개의스펙트럼인자들 로나누어처리함으로써, 소리신호를단순히시간의변화에따른강도변화로다루었을 때보다좀더높은차원의소리신호처리기법들을다룰수있게된다. - 6 -
1.5 WAVE sound-file format WAVE 파일포맷은마이크로소프트사의 RIFF(Resource Interchange File Format) 의 subset 으로서멀티미디어파일저장을위한용도이다. RIFF 파일은파일헤더가있고, 이후에일련의 data chunk 가뒤따라나온다. WAVE 파일은두개의 sub-chunk(fmtchunk: 데이터포맷을다룸. Data-chunk: 실제의샘플된데이터를포함.) 로구성된단일 WAVE chunk 를갖는다. 이러한형태를일반적으로 "Canonical form" 이라부른다. 거의일반적인 WAVE 파일은이와같은규칙을따르나, non-pcm 또는등록된특정데이터포맷을사용하는경우도있으나, 거의무시할정도임.) 부록그림 1-1. Canonical WAVE file format - 7 -
canonical WAVE format 은먼저 RIFF 헤더로부터시작한다. Offset Size Name Description( 의미 ) 0 4 ChunkID ASCII 코드로 "RIFF" 를표현함. (0x52 49 46 46 big-endian form). 4 4 ChunkSize 이값은이필드이후에나오는 chunk 의나머지부분에대한크기임. 만일, 한개의필드만포함된다면, 36 + SubChunk2Size, or 더욱정확하게는 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size) 임. 8 4 Format ASCII 코드로 "WAVE" 를표현함. (0x57 41 56 45 big-endian form). WAVE 포맷은두개의 subchunk 즉, fmt" 와 data subchunk 로구성된다. fmt subchunk 는사운드데이터의포맷을기술한다 : 12 4 Subchunk1ID ASCII 코드로 "fmt " 를표현함. (0x66 6d 74 20 big-endian form). 16 4 Subchunk1Size PCM 데이터의경우에 16. 이값은 suchunk 의크기를지정함. 20 2 AudioFormat PCM = 1 (i.e. Linear quantization) Values other than 1 indicate some form of compression. 22 2 NumChannels Mono = 1, Stereo = 2, etc. 24 4 SampleRate 8000, 44100, etc. 28 4 ByteRate = SampleRate * NumChannels * BitsPerSample/8 32 2 BlockAlign = NumChannels * BitsPerSample/8 34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc. 2 ExtraParamSize if PCM, then doesn't exist X ExtraParams space for extra parameters 데이터 subchunk 는실제사운드데이터의크기를포함한다 : 36 4 Subchunk2ID Contains the letters "data" (0x64 61 74 61 big-endian form). 40 4 Subchunk2Size = NumSamples * NumChannels * - 8 -
BitsPerSample/8 44 * Data 실제의사운드데이터. 예로써, 16 진코드로표현된 72 바이트의 WAVE 파일헤더에대해고려해본다. 52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d 부록그림 1-2. WAVE file format 의분석예 References: 1. http://www.ora.com/centers/gff/formats/micriff/index.htm 2. http://premium.microsoft.com/msdn/library/tools/dnmult/d1/newwave.htm 3. http://www.lightlink.com/tjweber/stripwav/wave.html - 9 -
- 10 -
[ 실습문제 ] 1. MS Windows 계열의 OS 에서제공되는 [ 녹음기 ] 를이용하여, 본인의음성을 WAVE 파일형식으로녹음및저장한다. 이때, 어떠한종류의저장형식이있으며, 그특징에대해조사하시오. 2. 그림 1-2 에나와있는것과같이, 주파수가다른 3 종류의 sine 파형을만들어중첩시키고, 그결과를확인하시오. 직접 C-code 로작성하여그결과를파형으로보이시오. 3. 주어진종류의 WAVE 파일즉, demo.wav, ding.wav, aloveidea.wav 파일의헤더파일을읽은후에, 각필드값이어떤값들이들어있는지분석된결과를구하시오. ( 다음쪽의 C-program 을참조하시오.) [Report#1] : WAVE 포맷의음성파일을읽어들여각 structure 의멤버값을얻는프로그램을주어진 C-code 를확장하여작성하시오. - 11 -
실습예제 1-3 /**************************************************************** * fopen() 함수의사용및파일열기 * *****************************************************************/ #include <stdio.h> main () { FILE *fi; unsigned char tmp; double sp, ep, loop, length; void exit(); if ( (fi=fopen("test-16bit.wav","rb"))==null) { printf("\t FILE NOT Found!!!\n"); exit(0); } printf("\t Enter Start Byte Number:"); scanf("%lf",&sp); printf("\n\t Enter End Byte Number:"); scanf("%lf",&ep); printf("short:%d, long:%d\n", sizeof(short), sizeof(long)); if(sp>=ep) { printf("\n\t Error on selecting points\n"); exit(0); } length = ep-sp; loop = 0.; printf("\n"); while ( loop <= ep ) { tmp = getc (fi); if (((int)loop)%8==0 && sp<=loop+1 && loop<=ep) printf("\n[%06d] 0x",(int)loop+1); if (sp<=loop+1 && loop<=ep) printf(" %02x (%c)", tmp, tmp); loop += 1.0; } printf("\n"); fclose(fi); } - 12 -
/**************************************************************** * WAVE 파일헤더의이해및해석 * ****************************************************************/ #include <stdio.h> #include <stdlib.h> struct WAVE_HEADER { char riff_id[4]; /* "RIFF" */ long riff_size; /* riff size */ char wave_id[4]; /* "WAVE" */ char format_id[4]; /* "fmt " */ long format_size; /* format size */ short format_type; /* 1 for PCM */ short ChannelNo; /* 1 for mono, 2 for stereo */ long SamplesPerSec; /* 11025 for 11KHz */ long AvgBytesPerSec; short BytesPerSample; /* Block align */ short BitsPerSample; /* 8 for 8bits, 16 for 16bits */ /* bits per channel */ char data_id[4]; /* "data" */ long data_size; /* data size */ }; void main() { FILE *fp; struct WAVE_HEADER waveheader; double PlayTime; if( (fp=fopen("aloveidea_mono.wav","rb")) == NULL) { printf("\t Reading File Not Found!!!\n"); exit(0); } fread(&waveheader, 1, sizeof(waveheader), fp); fclose(fp); PlayTime=(double)(waveheader.riff_size - 8)/((double)waveheader.SamplesPerSec*(double)waveheader.BytesPerSample); - 13 -
printf("channel Number (1=mono,2=stereo) = %d\n",waveheader.channelno); printf("samplespersec = %d [sample/sec]\n",waveheader.samplespersec); printf("bytespersample(blockalign) = %d [bytes/sample]\n",waveheader.bytespersample); printf("avgbytespersec = %d [bytes/sec]\n",waveheader.avgbytespersec); printf("data_size = %d [bytes]\n",waveheader.data_size); printf("riff_size = %d [bytes]\n",waveheader.riff_size); printf("playing Time = %lf [sec]\n",playtime); } - 14 -