2016년 12월 17일 토요일

HI-TECH C 용 BGM 라이브러리 만들기 #4 - FM 지원

기존 BGM 루틴에 FM 사운드도 추가하였습니다.

여기서 FM은 MSX-MUSIC(YM2413-OPLL) 칩을 의미합니다요.

왠만한 후기형 MSX2/2+에는 MSX-MUSIC이 내장되어있어요.

물론 turbo R에서도 지원되구요.


MSX-MUSIC은 두가지 동작 모드가 있어요. 멜로디/리듬 모드인데요.

멜로디 모드는 9채널 모두 멜로디 악기로 동작하는 모드이고,

리듬 모드는 마지막 3채널(7,8,9번)을 리듬 악기(5가지)로 구동하는 모드입니다.


아마 20여년전에 BASIC으로 구동해보셨던 분 많으시죠? ㅎ.ㅎ

이제 C로 한번 해보셔요~ㅋㅋ


그리고 라이브러리 내에서 CPU 모드를 체크해서, 칩 액세스 타이밍을 적절하게 세팅합니다.

Z80 일반모드, R800 고속모드 상관없이 자동으로 처리되니까 참고하세요.



그럼, 이쯤에서 1편에서 나왔던 요구사항을 점검해봅니다.


1. 배경음악 재생

2. 효과음 등의 사운드 믹스

3. PSG 구동

4. OPLL 구동

5. MML 사용


오홍~ 대충 완성이군요. ㅎ.ㅎ

유X군님께, 올 연말까지 라이브러리 정리해드리기로 약속했는데...

미션 성공이네요. 만세~!



백문이불여일견! 소리를 들어보겠습니다요! (쓰고보니 반대네요~. 백견이불여일문? ㅋ)

듣기전에 기존 테트리스 프로그램의 PSG, FM 채널 설정부분의 코드를 잠깐 보시지요.




BGM 채널 0,1,2는 PSG로 출력이 되구요.

채널 3,4,...,11은 FM으로 출력이 됩니다.


만약 FM 리듬모드를 켜면,

채널 3,4,...,8는 FM 멜로디 출력

채널 9는 FM 리듬 출력으로 처리됩니다.



그럼, 동작 영상 나갑니다~

PSG/FM 테스트 프로그램과 테트리스 구동 모습입니다.





그럼, 즐거운 주말되세요!


2016년 12월 12일 월요일

2016 송년회 - 잘 다녀왔어요~

지난 토욜 천국동 송년회가 있었습니다.

준비하느라 수고하신 유령군님, 우유속의소주님께 감사를 드립니다. 최고! ㅎ.ㅎb


이번엔 작년보다 더 많은 분들이 오셔서, 더욱 신나는 시간이 되었습니다.

그럼, 사진 몇장 나갑니다~


밥먹다 찍은 셀카! (좀 흐릿하네요~ㅋ)





제 맞은편에 계시던 미남 3인조! 으흐흐...




이번 송년회 나눔, 경매에서는 가위바위보 전문가님(!)들 덕분에 많은 득템은 힘들었지만...

키티야님이 주신 GT 골드 메탈 스티커~

(키티야님이랑 사진을 같이 찍었어야 하는건데!)




요건 저의 나눔/경매품이구요.




아래는 최종 득템상황입니다.

이올로님의 멋진 센스! 천국동 이름이 새겨진 볼펜이랑 메모지입니다.

ASTERiS님의 MS 마우스도 있구요. (무려 볼마우스입니다 ㅎ.ㅎb)




저는 10시쯤에 2차 치킨집에서 남영역으로 후다닥 뛰쳐나왔어요.

집에오니 거의 12시가 되었네요.

시간이 후딱 지나가서 아쉬웠지만... 그래야 다음에 만나면 또 할 얘기가 남아있겠죠? ㅋㅋ


천국동 여러분 사랑합니다~ ㅎ.ㅎ



PS. 키티야님의 골드 스티커를 붙여보았습니다.




이랬던 저의 GT가 아래처럼 번쩍번쩍하게 되었어요! 유후~ ㅎ.ㅎ




2016년 12월 9일 금요일

HI-TECH C 용 BGM 라이브러리 만들기 #3 - 소리가 나는 테트리스?

2011년이니까 5년전이군요.

그래픽 라이브러리 데모용으로 테트리스를 만들었는데요.

아마 기억하시는 분 계시리라 생각됩니다. ㅋ




드디어! 조용한 테트리스 게임에 소리를 넣을 수 있게 되었습니다~ 오예~

무려 5년이 흘렀지만요 ㅋ


음악을 넣으려고 구글에서 악보 검색을 하다보니, 문득 생각이 나더라구요.

워낙 유명한 게임인데, 이거 MML로 만든사람 있지않을까?


역시나, 검색하니 딱 나오네요. ㅎ.ㅎb

감사히 쓰겠습니다~ 




C 코드로 복사해서 넣고 돌렸어요. 음~ 소리 잘나오네요!




배경음악만 있으면 심심하니까, 효과음도 몇개 넣어봅니다.

블럭 이동/회전/낙하/라인삭제, 4가지 효과음을 넣었습니다.

저의 실력으로는 이정도가 한계군요. ㅎ.ㅎ




전체 소스는 github에서 받으시면 되겠구요.

https://github.com/sharksym/CPMEMU_HI-TECH_C


게임만 구동해보실 분은 첨부파일 받으시면 되겠습니다

Download: TETRIS_20161209.zip



그럼 돌아가는 모습을 한번 볼까요?

모니터가 밝아서 그런지, 폰 영상이 춤을 추는군요! ㅋ





그럼, 즐거운 밤되셔요~


2016년 12월 6일 화요일

HI-TECH C 용 BGM 라이브러리 만들기 #2 - 소리가 날까?

MML이 뭔지 아시죠?

Music Macro Language 인데요. 음악을 표현하는 언어(?) 정도로 생각하시면 되겠습니다.

BASIC의 PLAY 명령에서 사용되는 문법이 대표적인 MML이라고 보시면 되겠습니다.


더 궁금하시면 wiki 문서를 찾아보셔요~ ㅎ.ㅎ

https://en.wikipedia.org/wiki/Music_Macro_Language


아래는 MSX-MUSIC BASIC 매뉴얼에 나오는 샘플곡입니다.

채널 2개로 출력하는 프로그램이네요.




OPLL 악기 설정만 빼면, 일반 PSG용 MML과 같은 구조로 되어있습니다.

요걸 C 코드로 옮기면 아래처럼 됩니다.




BASIC 코드와 C 코드의 MML 문자열이 똑같은걸 볼수가 있지요?

"BASIC에서 PLAY 코딩하던 것처럼 배경음악을 만들 수 있다"를 기억하시면 되겠습니다~


xxx_enqueue()가 모두 끝나면, xxx_play()로 배경음악을 시작하고 xxx_stop()으로 멈출 수 있습니다.

음악이 재생완료되었는지는, xxx_get_stat() 함수로 상태확인이 가능합니다.


위의 코드에서는 음악 연주를 가만히 기다리고 있으면 심심하니까,

놀고있는 PSG 채널로 짧은 효과음을 출력해봅니다.

shot() 함수는 아래처럼 구현되어있습니다.




총알소리에 쓰인 MML에 Z 명령어가 보이시죠?

요건 BASIC MML에서는 없던건데, PSG 노이즈 주파수 설정하는 용도로 사용됩니다.

참고하셔요.


실행하면 아래처럼 현재 버퍼 상태를 확인할 수 있구요.

스페이스 키 누르면 총소리가 나게됩니다~ ㅎ.ㅎ

PSG 두개 채널은 배경음악 용, 나머지 한개 채널은 총소리 출력으로 사용됩니다.




그럼 이번엔 3채널 모두 사용하는 BGM을 연주해봅니다.

베토벤 할아버지께서 작곡하신 "환희의 송가"가 되겠습니다요~




3개 채널이 모두 사용되니까, 스페이스 눌러서 총소리를 출력하게 되면,

PSG 마지막 채널은 배경음악과 총소리로 번갈아서 출력됩니다.

일단 설명만 드리고 넘어갑니다.

이 글 마지막에 동영상이 있으니까 그걸로 확인하셔요! ㅎ.ㅎ



라이브러리가 어떻게 사용되는지 대충 아시겠죠?


마지막으로 MML 데이터를 연속으로 enqueue()해서, 배경음악을 무한으로 연주해봅시다~

코드는 아래처럼 만들었습니다.

템포 240으로 "학교종이 땡땡땡~"을 연주해보았어요 ㅋㅋ




처음 enqueue()를 한번 해주고 play() 시작합니다.

그 후, 버퍼에 여유가 있으면 계속 데이터를 enqueue()합니다.

ESC 누르면 종료하도록 해주고요. 간단하죠? ㅎ.ㅎㅋ



그럼, 실제 동작하는 모습을 영상으로 보시겠습니다.

보기전에 약간의 설명들어갑니다 ㅋ

영상에 보시면 아래처럼 나오는데요.

화면의 빨간색 띠는 배경음악 처리에 사용되는 CPU 점유율입니다.

터보알 고속모드랑 Z80 3.58MHz 모드 두번 구동하니까 한번 비교해보셔요~





마지막으로 영상 나갑니다~




그럼, 즐거운 하루 되세요!


HI-TECH C 용 BGM 라이브러리 만들기 #1 - 플레이어 함수

오랜만에 라이브러리를 추가하고 있습니다.


사실 올해초에 만들다가 잠시 쳐박아둔 코드였는데요. (딴짓 하느라 바빠서요~ㅋ)

올해가 넘어가기 전에 PSG 용 BGM 루틴이라도 마무리해야될 것 같아서, 지난 주에는 코딩을 좀 했습니다. ㅎ.ㅎ


암튼, 글을 쓴다는 것도 까먹고 있다가... 대충 완성이 되고나서야 올리는 글이 되었습니다!



대강의 요구사항은 이렇습니다.


1. 배경음악 재생

2. 효과음 등의 사운드 믹스

3. PSG 구동

4. OPLL 구동

5. MML 사용



간단한 게임 프로그램에서 쓸 수 있도록 만드는 것이 목표입니다.

기능이 복잡하게 될 필요는 없고, 배경음악 + 효과음을 쉽게 처리할 수 있는 방식이면 충분합니다.


각 요구사항들을 자세히 만들어 보았습니다.


먼저 1번 배경음악 재생과 5번 MML 사용에 관한 내용입니다.

- 음악 데이터 양이 많은 경우, 동적으로 데이터를 버퍼에 enqueue 할수 있어야 함.

  양이 적은 경우는 몽땅 enqueue 후 반복 재생도 가능해야 함.

- 음악 데이터는 버퍼 enqueue 시점에 일부분 MML 번역을 하여, BGM 코드의 CPU 부담을 줄임

- BASIC MML과 비슷한 문법의 MML 데이터를 사용. (별도의 PSG 트래커 필요없음)


2번 효과음 믹스에 관한 내용입니다.

- 배경음악 및 효과음은 PSG 3채널 모두 활용가능해야함

- 배경음악과 효과음 동시 출력 시, 효과음 우선으로 출력함


3번 PSG 및 4번 OPLL 구동

- PSG only 또는 OPLL only 또는 PSG + OPLL 동시 사용 시나리오 가능해야 함.

- PSG H/W 엔빌롭 대신 S/W 볼륨 방식의 엔빌롭 사용. (실제 악기와 유사한 패턴)

- PSG 노이즈 설정 가능해야함.

- OPLL 내장 보이스 및 사용자 보이스 설정 가능해야 함.



대충 감이 오시지요? ㅎ.ㅎ

PSG만 사용하는 게임들을 살펴보면, 빈약한 3채널만으로도 배경음악과 효과음을 잘 처리하고 있는데요.

그런 게임들처럼 비슷하게 구현할 수 있는 방법으로 만들어갑니다.



그럼, 실제로 코드를 만들어봐야겠네요.


배경음악처럼 메인 코드에 영향받지 않고, 정확한 타이밍으로 출력하려면 인터럽트가 필수겠습니다.

주기적인 인터럽트 처리는 VDP의 V-Blank 인터럽트를 활용하면 되겠죠?

메인 BIOS의 TIMI 훅 사용과 비슷하게 만들어줍니다.


아래는 BLSTDINT.H 파일에 선언된 인터럽트 리스트입니다.




"어... MSX는 Z80 IM 1 모드라서 인터럽트 여러개 처리가 안될텐데요?"

라고 생각하시는 분도 계시겠죠?


제 라이브러리를 예전부터 쓰셨으면 이미 알고 계시겠지만~ ㅎ.ㅎ

모두 S/W 로 구동됩니다.

메인 인터럽트 핸들러는 뱅킹 라이브러리 내에 이미 포함이 되어있구요.

프로그래머가 사용하기 편하도록, 특정 조건에 따라 콜백함수를 호출하는 방식입니다.

(참고로 IRQ 번호는 작은 숫자가 우선순위가 높습니다.)


배경음악 재생을 위해, IRQ_SND_CYCL IRQ가 사용됩니다.

실제로는 VDP V-Blank 주파수에 따라, 60Hz 또는 50Hz 주기로 호출이 되겠습니다.


음... 그러고보니 V-Blank 주기에 따라 배경음악 Tempo를 조절하는 기능도 필요할지 모르겠네요. ㅎ.ㅎ

60Hz/50Hz 바뀔 때, 음악 속도가 영향받지 않으면 좋겠지요?

.

.

하지만 실제로 게임 만들게 되면, V-Blank 주기를 하나로 고정하니까... 별로 의미없을 수도 있어요.

실제로 코나미 게임들을 보면, VDP 신경 안씁니다요~ㅋ

유럽산 MSX에서 게임 구동하면, V-Blank 50Hz에 맞춰서 게임전체가 느려져요.

원래 60Hz 기준으로 만든 게임들이라 그렇습니다.


암튼 그렇구요.

이제 함수들을 대충 만들어봅시다~ ㅎ.ㅎ





초기화하고 데이터 밀어넣고(enqueue) 재생!

경우에 따라 잠시 멈춤이라든가 효과음 섞어 출력!

"효과음을 섞는다"라는 표현을 쓰긴 했지만...

실제로 PSG에서 배경음악과 효과음 소리가 믹스되어 동시에 나오는건 아니구요.

효과음 출력을 넣으면, 그 시점의 배경음악 소리가 잠시 들리지 않게 처리가 됩니다.


만약 배경음악 3채널을 쓰고, 효과음을 넣어야한다면...

주 멜로디가 되는 채널을 피해서 효과음을 넣어야겠지요?

효과음이 두개라면?? 채널 우선순위를 잘 정하시면 됩니다. 누가? 프로그래머가요 ㅋㅋ

어쨌거나 라이브러리는 배경음악, 효과음 모두 3채널 활용가능합니다.


배경음악은 xxx_enqueue() 함수로 데이터 처리가 되구요.

내부적으로 512바이트 버퍼를 갖고 있습니다. (더블 버퍼 방식으로 되어있어요.)

미리 버퍼를 채울 수 있어요.

그리고 버퍼 상태는 xxx_get_pos(), xxx_get_avail() 함수를 쓰면 됩니다.


효과음은 xxx_overlay() 함수로 데이터를 밀어넣습니다.

소리가 나야하는 시점에 xxx_overlay() 함수를 호출하면 즉시 데이터가 출력됩니다.

이전에 출력되던 효과음 데이터가 남아있다면 지우고 새로운 데이터로 교체됩니다.

총알을 빠르게 쏘면, "다다다다~아~" 식으로 소리가 들리겠네요. ㅎ.ㅎ


글이 너무 길어지면 읽기 힘드니까 1편은 여기서 줄입니다.


그럼, 이만...