-----------------------------------------------------------
피크메타 제작
-
트론의 유령 -
-----------------------------------------------------------
안녕하십니까? 저번에 약속?한대로 이번엔 피크메타 제작에 관
한 강좌?를 올리겠습니다.미약하나마 여러분들에게 도움이 됐으
면 하는 바램입니다. 역시 강좌?는 편의상 경어를 생략하겠습니
다. 여러분들의 양해를 구하며.. 시작합니다. ^^
-----------------------------------------------------------
피크메타? - 피크메타란 무엇인가..일단 그것보다 알고 넘어가
는 것이 순서가 아닐까 한다.. 피크메타란..사전을 찾아보니 그
뜻이 아래와 같다.
-----------------------------------------------------------
[peak]
1 (뾰족한) 산꼭대기, 봉우리
3 절정, 최고점, 극치; 최대량
4 성수기; (성수기의) 할증 요금
5 (군모 등의) 챙
6 음성 (음절의) 핵(核)
[meter]
1 미터 ((길이의 단위; =100cm))
2「계(기); 미터」의 뜻
-----------------------------------------------------------
아마도 내가 말하려는 피크메타에서의 피크는.. 절정, 최고점,
극치;최대량.. 의 피크일 것이다. 메타.. 미터..발음의 관한 문
제 이므로.. 본 발음이 '피크미터' 인지 '피크메타' 인지는 잘
모르겠으나, 여기 강좌에서는 그냥 '피크메타'로 통일해서 부르
기로 한다. 메타는 기기의 뜻으로.. 피크메타라 함은, 최고점..
극치;최대량을 보여주는 기기(기계)정도로만 생각하면 될 것 같
다. 말로만 설명하려니까 본인의 머리에서 세포들이 노조파업을
하려고 하고 여러분들의 머리속 세포들 역시 파업에 동참하려고
준비중인 것 같다.그래서 그림으로 한번 보여주는게 빠를 것 같
아 그림을 첨부했다.
-----------------------------------------------------------
[그림 1-1] 피크메타란.. 대충 저런 것이다..(본인의 디자인 실
력에
감탄했다고?..-_- 본인 역시 감탄하는 중이다)
본인이
이번에 제작중인 프로그램인데... 저번부터
꼭 한번
우드(Wood) 스타일의 스킨을 써보고 싶었다
그런데,
결론은.. '꽝' 이다.. ㅠㅠ
-----------------------------------------------------------
현재 재생중인 음악의 이진데이타(WAVE 파일)에서 최고치를 뽑
아내어 그래프로 보여 주는 것이다. 저걸 어떻게 만드냐고? 지
난번까지 올라온 강좌( 1편 - 5편 )를 잘 보신 분이라면, 별 어
려움 없이 만들 수 있을 것이라 본다. WAVE 파일의 구조와 처리
방법에 대해서는 본인이 올린 지난 강좌를 참조하기 바란다.(검
색에서.. 제목에 [API].. 하면 나올 것이다..) 피크메타를 제작
하기 위해서는 일단 현재 진행중인 곡에서 각 부분 마다 일정한
주기로 최대값을 뽑아내야 하는데.. 본인은 그 시간을 30 분의
1초 ( 1/30 Secs )로 정했다. 여기서 필요한 것이 TIMER 함수인
데..
hTimer = ( HANDLE ) SetTimer ( hwnd, 1, (1000/30), NULL )
;
으로 설정하면 아마도.. 30분의 1초마다 TIMER 메세지가 발생할
것이다. 이 부분을 WM_CREATE 부분에 설정해 주는데.. 윈도우프
로그래밍에서 WndProc (윈도우 프로시저)에서 제일 처음으로 처
리하는 메세지는 WM_CREATE이다. 이 메세지는 윈도우가 처음 생
성될 때 발생하는데 이 메세지에서 프로그램 시작시 꼭 한 번만
초기화, 설정 되어야 할 처리를 해 주게 된다. 프로그램 실행시
필요한 메모리를 잡아준다던가 전역변수에 초기값을 대입한다던
가 하는 일 처리를 여기서 해주면 된다.
LRESULT CALLBACK WndProc ( HWND hwnd, UINT iMessage, WPARAM
wParam,
LPARAM lParam )
{
switch(iMessage)
{
case
WM_CREATE : hTimer = ( HANDLE ) SetTimer
(
hwnd, 1, (1000/30), NULL ) ;
이렇게 처리하면, WM_CREATE 메세지에서 SetTimer 함수를 사용
하여 타이머를 생성시킨다. 즉, 프로그램이 시작하면서 동시에
타이머가 생성된다는 말이다. 이제 30분의 1초마다 hWnd 윈도우
에 WM_TIMER 메세지가 전달될 것이다. 남은 일은 WM_TIMER 에서
30분의 1초마다 피크메타를 그려주면 되는 것이다. 일단 피크메
타를 그리기 전에 그 정보를 얻어와야 하는 것이 일의 순서일텐
데.. 어떻게 얻어 오는가..
저번 강좌를 보신 분이라면 MM_WOM_DONE 이라는 메세지를 기억
할 것이다. 이 MM_WOM_DONE메세지 부분에서 일정한 크기로 나눈
WAVE 파일 조각을 읽고 재생하고 다시 또 읽고 이 짓을 WAVE 데
이타가 끝날때까지 하도록 설정 했었는데, 핵심 부분만 간단히
복습하고 넘어가자.
-----------------------------------------------------------
[ MM_WOM_DONE 메시지 처리 부분 중요 코드 ]
...
mRet = mmioRead ( hMMIO, (char*) pBuffer[CNT], read_size
);
if ( ( mRet == -1 ) || ( mRet == 0 ) )
{
waveOutClose ( hWaveOut )
;
return FALSE;
}
waveOutWrite ( hWaveOut, pWaveHdr[CNT], sizeof (WAVEHDR)
);
GET_PEAK_MAX ( waveFormatEx.wBitsPerSample,
waveFormatEx.nChannels,
pBuffer[CNT],
read_size,
&maxL,
&maxR
);
CNT++;
CNT %= BufferNum ;
...
-----------------------------------------------------------
이 부분이다. WAVE 파일의 데이타가 끝날때까지 read_size만큼
계속해서 waveOutWrite () 로 출력해 내는 부분이었다. 이 크기
는 여러분 마음대로 정해도 되지만, 지난번 강좌에서는 이 크기
를 아래와 같이 정했다.
-----------------------------------------------------------
[ read_size 의 크기 ]
read_size = (( waveFormatEx.Format.nSamplesPerSec *
waveFormatEx.Format.nChannels
*
waveFormatEx.Format.wBitsPerSample
/8 / 10);
-----------------------------------------------------------
read_size 의 크기안에서 최대치를 뽑아내면 되는 것이다. 이제
어떻게 그 값을 뽑아낼지 생각해 보자. 일단 GET_PEAK_MAX 라는
함수를 하나 만들자. 넘겨줄 인자로는 wBitsPerSample값과 채널
그리고, 현재 재생중인 데이타가 담긴 버퍼, read_size, 그리고
마지막으로 maxL, maxR 이라는 변수를 넘겨준다.
-----------------------------------------------------------
[ GET_PEAK_MAX() 함수의 인자 ]
GET_PEAK_MAX ( waveFormatEx.wBitsPerSample,
waveFormatEx.nChannels,
pBuffer[CNT],
read_size,
&maxL,
&maxR
);
-----------------------------------------------------------
여기서 maxL, maxR 은 최대치 값(피크)을 저장할 변수이다. 이
렇게 선언한 뒤에 함수를 실제로 만들어 보자.함수를 만들기 전
에 일단 WAVE 데이타의 샘플당 비트수와 채널에 따라 저장이 다
르게 된다는 점을 상기하기 바란다. (중요하다)지난번 강좌에도
그렸었지만.. 이번에 한번 더 그려서 설명한다. (심심해서..--)
-----------------------------------------------------------
[ 샘플당 비트수와 채널에 따른 저장 공간의 차이 ]
[ 8 Bit Mono ]
0 1 2 3
4 5 6 7 8
9 A B C D
E
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
: D : D : D : D : D : D : D : D : D : D : D : D : D : D
:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
[ 8 Bit Stereo ]
0 1 2 3
4 5 6 7 8
9 A B C D
E
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
: L : R : L : R : L : R : L : R : L : R : L : R : L : R
:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
[ 16 Bit Mono ]
0 1 2 3
4 5 6 7 8
9 A B C D
E
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
: D : D
: D : D :
D : D : D
:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
[ 16 Bit Stereo ]
0 1 2 3
4 5 6 7 8
9 A B C D
E
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
: L : R
: L : R :
L : R : L
:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
[ 32 Bit Mono ]
0 1 2 3
4 5 6 7 ...
+---+---+---+---+---+---+---+-- ...
:
D
: ...
+---+---+---+---+---+---+---+-- ...
32 Bit Mono 데이타는 4 Byte 나 먹는다. ( 0 ~ 4294967295
)
----------------------------------------------------------
이 그림같지도 않은 그림?을 괜히 심심하다고 그린건 절대 아
니다 -_-+.. 이 그림이 중요하기 때문에 다시 상기시키려고 그
린 것이다. ( 그러나, 자세히 보면 저번 강좌때하고는 좀 다르
다. 신경써서 좀 고쳤는데, 별로 티도 안난다.. )이 크기에 따
라 피크를 뽑아낼 방법이 달라지기 때문이다.
----------------------------------------------------------
----------------------------------------------------------
[ 본격적인 [피크메타] 제작으로
----------------------------------------------------------
자,이제 피크메타를 제작하기 위한 준비작업은 모두 끝마쳤고
실제로 구현에 들어갈 차례가 왔다.. ( 드디어.. )일단은 간단
하게 8 Bit 만 구현해 보자.-> 참고로 피크메타를 제작하는 방
법이야 많이 있을 것이다. 그 원리도 다를테고..여기서 강좌라
고 본인이 쓰는 글은 100% 이것이 정답이다!라고 쓰는 것이 아
니다. 절대! 아니다! 다만, 본인은 이렇게도 구현해 봤는데 이
런 방법에 대해서 여러분에게 알려주고 싶은 것이다.별로 도움
이 될지 안될지는 모르지만..
----------------------------------------------------------
아까 피크메타를 뽑아낼 함수를 제작하면서 그 선언을 다음과
같이 했었다.
-----------------------------------------------------------
[ GET_PEAK_MAX() 함수의 인자 ]
GET_PEAK_MAX ( waveFormatEx.wBitsPerSample,
waveFormatEx.nChannels,
pBuffer[CNT],
read_size,
&maxL,
&maxR
);
-----------------------------------------------------------
그럼, 이제 함수 몸체를 만들어 보자. 일단, 넘겨 받은 샘플당
비트수와 채널에 따른 분리를 해야 하므로,그에 대한 처리를 해
야 하는 것이 당연하다. 여기서는 8 Bit만 알아보기로 했으므로
그에 대한 처리부분에 대해 설명한다. ( 나머지 16, 24, 32 Bit
는 행복하게도? 여러분의 몫으로 남겨두겠다. ) 자 바로 다음이
다!
-----------------------------------------------------------
GET_PEAK_MAX ( int waveFormatEx.wBitsPerSample,
int
waveFormatEx.nChannels,
void
*pBuffer,
LONG
read_size,
LONG
&maxL,
LONG
&maxR )
{
BYTE *pB, bMaxL, bMaxR ; // 1 Byte (BYTE)
- bMaxL, bMaxR
int i;
LONG cnt = read_size / ( waveFormatEx.wBitsPerSample / 8 ) ;
cnt /= waveFormatEx.nChannels ;
//-- 8 Bit 에 대한 처리 부분
if ( waveFormatEx.wBitsPerSample == 8 )
{
pB = ( BYTE * )
pBuffer;
bMaxL = bMaxR = 0 ;
if ( waveFormatEx.nChannels == 1
) //-- Mono
{
for ( i =
0; i < cnt; i++ )
{
if
( *pB > bMaxL )
bMaxL
= *pB ;
pB++
;
}
*maxL =
*maxR = bMaxL ;
//wsprintf
( L_MAX, "%03d - Mono", bMaxL ) ;
//wsprintf
( R_MAX, "%03d - Mono", bMaxL ) ;
}
else if ( waveFormatEx.nChannels
== 2 ) //-- Stereo
{
for ( i =
0; i < cnt; i++ )
{
if
( *pB > bMaxL )
bMaxL
= *pB ;
if
( *( pB+1 ) > bMaxR )
bMaxR
= *( pB+1 );
pB
+= 2
;
}
*maxL =
bMaxL ;
*maxR =
bMaxR ;
//wsprintf
( L_MAX, "%03d - Stereo", bMaxL ) ;
//wsprintf
( R_MAX, "%03d - Stereo", bMaxR ) ;
}
}
}
-----------------------------------------------------------
주석 처리한 " wsprintf ( L_MAX, "%03d - Mono", bMaxL ) ;
"
부분은 프로그램의 진행상 별로 필요는 없는것이지만,실제로 뽑
아낸 피크값이 얼마인지 보기위해 만들어 둔 것이다.WM_PAINT메
시지에서 TextOut (hdc,X,Y,L_MAX,strlen(L_MAX));과 같이 해서
그 값을 볼수 있다.함수를 보면 대충 그 기능이 이해가 갈 것이
다. 제일 처음에 포인터 pB 에 ( BYTE * ) 단위로 pBuffer를 저
장한다. 이때 주의할 점은 앞으로 16 / 24 / 32 Bit를 처리할때
그에 맞게 형 변환 해줘야 하므로 넘겨받을 pBuffer를 void형으
로 받아야 한다는 것이다.함수헤더를 보면 이해가 갈 것이다.그
리고 bMaxL 와 bMaxR 를 '0' 으로 초기화 해준다.
-[ 쉬다 갈까나?
]------------------------------------------
'C'프로그램을 하다 보면 자잘한 버그와의 싸움으로 밤을 새는
경우가 허다한데, 대부분의 초보시절에는 이런 변수선언에서 초
기화하는 부분 에서도 애를 먹기도 한다. 여기서 bMaxL, bMaxR
은 함수 안에서 선언된 지역변수(Local) 이면서 자동변수(Auto)
이다. 자동변수는 대게 auto 라는 형지정자를 생략하기도 하는
데, 보통 쓰는 int a; , int i; 하는 변수들이다. 선언된 지점
에서 블록이 끝나는 시점에서 자동으로 파괴?되기 때문에 그런
이름이 붙었다. 중요한 점은 저놈들은 절대 만들어지는 순간'0'
으로 초기화 되지 않는다는 것이다.
선언된 순간 표현한계까지의 수 중에서 임의의 수로 채워져 있
을것이다. (흔히들 '쓰레기값' 이라 하는 그것이다. ) 자동변수
는 선언되었을때 하나의 기본동작만이 일어난다. 즉, 선언된 각
각의 변수에 대해 기억공간이 할당될 뿐이다. ( 아니 왜 갑자기
변수 얘기로 빠져들었지.. --+ ?? ) .. 잠깐 한박자 쉬고..선언
했으면 초기화! 하자..
-----------------------------------------------------------
그리고 if ( waveFormatEx.nChannels == 1 )
//-- Mono
else if (
waveFormatEx.nChannels == 2 ) //-- Stereo
에 따라 모노일때와 스테레오일때의 피크메타 처리를 해주면 된
다. 몇줄 안되는 부분이고 간단한 부분이므로 별다른 설명은 없
다.중요한 점은 이제 maxL, maxR 에는 피크 값이 들어가 있으므
로 그 값으로 선을 그리던지 네모를 그리던지..뭘 그리던지간에
그 처리만 해주면 되는 것이다. 본인은 아래와 같이 그냥..사각
형 모양으로 그래프를 처리했다.
RectBrush = (HBRUSH)GetStockObject(GRAY_BRUSH) ;
OldBrush = (HBRUSH)SelectObject(hdc, RectBrush) ;
Rectangle(hdc, 30, PEAK_MAX_HEIGHT_1, 33,
PEAK_MAX_BOTTOM);
비트맵으로 처리하면 더 이쁠텐데, 위에서 본인의 디자인 실력
에 감탄한지라.. 더 이상 감탄했다가는 심장마비에 걸려 죽을것
같아 비트맵 처리는 당분간 보류 하기로 했다.
-----------------------------------------------------------
-[ 끝내고 나서.. ]
----------------------------------------
어땠습니까?? 이번 강좌?도 나름대로 쓸만한 정보를 드리려고
노력했는데,다시 읽어 보니까 과연 여기서 뭘 얻어갈 수 있을지
의문이.. 어쨋든, 대강의 흐름은 아셨을 겁니다. 어설프게나마
피크메타를 그리는 법을 설명했는데 도움이 됐으면 좋겠습니다.
다음에는 무슨 강좌를 할까요?? 음..아마도 사인파형 그려서 보
여주기를 하지 않을까 싶네요. ( 강좌 하나 했으니 또 놀아야죠
.. 워크3도 하고.. ^^.. ) 강좌에서 사용한 용어들이 잘못 쓰인
부분이 있을지도 모릅니다. 그때 그때 지적해 주시면 바로 교정
하겠습니다. 아직 저도 미숙한지라.. 실수가 있을 수 있습니다.
다음 강좌때까지.. 모두들 열심히 프로그래밍! 합시다..
[ E-Mail ] : lovesgh@empal.com
- 트론의 유령
-
----------------------------------------------------------- |