잡담

[2018. 11. 25.] 근황

MonoGame을 이용해서 Entity Component System(ECS) 기반으로 2008년에 만들었던 SK-VM 게임을 리메이크 하는 중.

ECS 구조로 만들고 있는 가장 큰 장점은 개발하면서 생각한건데 관리되는 언어에서 사용하기 가장 효율적인 구조 같다는 점. 엔진에 ECS 만들어놓고 거기에 오브젝트 풀까지 적용해놓으니까 가비지 관리에 매우 용이해서 만족스럽다. 다만 아쉬운 점은 MonoGame이 멀티스레드 렌더링을 지원을 아직도 안 해서 엔진 자체는 멀티스레드 업데이트 -> 싱글스레드 렌더링을 하도록 해놨다. 물론 부동소수점 연산도 안 되고 램 용량도 2MB밖에 안 되던 피쳐폰 시스템에서 돌던 게임을 리메이크하는거라 뭘 어떻게 구현하든 2600X에서 CPU 사용량이 2%밖에 안 된다. 해상도가 176*178이라 GPU 사용량도 낮다.

다만 리메이크하는 게임이 완성본이 아니라 한창 개발 중이었던 소스+기획서 밖에 안 남아서 완성은 못하고 기본적인 시스템이랑 초반 맵만 구현 가능할 듯. 이렇게 된 이유가 2008년 당시에 친구가 노트북 포맷한다고 백업하게 외장하드 빌려갔다가 내 외장하드를 포맷해버려서… 그나마 개발 중이었던 소스는 학교에서 백업해놔서 그걸 받아와서 가지고 있던거고…

Unity로 리메이크하던 Shooting Hero는 오브젝트 풀 구현해야 되는데 너무 귀찮아서 보류. 새 리소스를 처음 사용하는 경우에 발생하는 스터터링도 해결해야 하는데 진짜 너무 귀찮아.

광고
미분류

지금 사고 싶은 것들

1. NVMe SSD(최소 500GB)

250GB + 480GB SSD를 하나로 통합하고 싶고 2.5인치 저장매체로 되어있는걸 메인보드에다 안 보이게 꽂아두고 싶음

아마존에서 블랙프라이데이 딜로 1테라 M.2 SATA SSD 샀고 한국 날아오는 중. 스탠다드 배송인건 친구랑 같은데 친구는 더 일찍 출발해서 이미 저번주에 택배사로 전달됐는데 난 이제 비행기 타고 오고 있다.

2. 2.5인치 3TB 외장하드디스크

지금 쓰고 있는 3.5인치 4TB 하드디스크에 불량 섹터가 생기는 중. 한 달 안에는 반드시 사야 될 거 같은데 일단 블랙프라이데이 세일 기다리는 중.

옥션에서 그냥 샀다. 마침 빅스마일데이 할인 기간이라 2만원 싸게 샀음. 문화상품권 쓰면 만원 더 깎을 수 있었을텐데 귀찮기도 하고 해서.

3. FHD 144Hz 모니터

게임 144Hz로 쓰고 싶음

4. 맥미니(2018)

오랜만에 Xcode로 오브젝티브-C나 스위프트 프로그래밍 하고 싶다

잡담

MonoGame이 Unity를 따라가지 못하는 이유?

최근에 Unity를 공부할 겸 2011년에 Android 기본 API로 만들었던 게임을 Unity로 포팅했었는데 몇 가지 귀찮은 점을 빼면 상당히 편리하게 만들었다.

대충 1차 완성을 끝내고 이번엔 2008년에 SK-VM API로 만들었던 게임을 Unity로 포팅하려는 와중에 이미지 크기가 너무 작아서(무려 해상도가 176 * 178이다.) 유니티 내에서 사용하기가 어려워(Unity의 기본 2D 타일 렌더링 기능이 정상 작동하지 않았다. 이미지 크기를 키운다면 가능은 하겠지만…) 이번에는 MonoGame으로 만들기로 결정했다.

여기서 문제점이 발생한다. MonoGame의 전신은 XNA Game Studio고, XNA Game Studio의 API는 DirectX 10이 막 출시됐던 1.0과 큰 차이가 없다. 그리고 DirectX 10은 싱글스레드 기반의 렌더링 시스템이고, 멀티스레드 기반 렌더링 시스템인 DirectX 11과는 기본적으로 API 설계 자체가 호환되지 않는다. 물론 MonoGame의 윈도우 버전 라이브러리는 DirectX 11을 이용해 개발됐지만 DirectX 11의 멀티스레드 기능은 전혀 사용하지 않는다.

MonoGame은 XNA Game Studio의 API를 크게 벗어나지 않는 한에서 API를 수정하고 있기 때문에 멀티스레드 기반 렌더링을 지원하지 않는다. 논의가 계속 진행됐지만 결국 다 포기했는지 2018년 4월 이후로는 논의가 없다.

고수준 엔진인 Unity와 저수준 프레임워크인 MonoGame을 비교하는건 의미가 없긴 하지만 MonoGame을 기반으로 하는 고수준 엔진과 비교한다면 의미가 있겠지. MonoGame부터가 멀티스레드를 제대로 지원할 수 없으니 Unity와 비교했을 때 성능 차이가 발생하게 된다.

이 때문에 MonoGame은 주로 소규모 인디 게임에서 활용되고 있고 대규모 게임으로 가면 갈 수록 볼 수가 없게 된다. 물론 성능 문제 이외에도 손쉬운 에디터의 유무도 갈리겠지만.

어쨌든 개인적으로 MonoGame은 슬슬 XNA의 틀을 벗어나 API를 전체적으로 현대적인 하드웨어를 제대로 활용할 수 있도록 개선하는 방향으로 가야되는 것 아닌가 하는 생각이 든다. 그것만 된다면 이를 이용한 고수준 게임 엔진이 나오기도 쉽겠지.

기기 리뷰

[리뷰] LG G7 ThinQ

샤오미 Mi A1을 산지 얼마나 됐다고 LG G7 ThinQ를 샀나 싶겠지만 암튼 샀다. 터치감도 마음에 안 들고 좀 더 빠릿빠릿한 폰을 쓰고 싶어서.

새 폰을 사기 위해 여러 후보군들을 가지고 각을 재다가 아미인 친구가 방탄소년단 광고 때문에 G7을 강력하게 추천해서 어쩔까 하다가 이주했다.

lg-g7-black

일단 무게는 넥서스 5X를 이길 수는 없지만 그래도 샤오미 Mi A1에 비해서는 약간이나마 가벼운 편. 들고 다니기에 불편하지는 않다.

스냅드래곤 845를 써서 그런지 속도는 빠르고 배터리 소모율은 적은 편. Mi A1이랑 배터리 용량이 같은데도 하루 일과가 끝나고 배터리 상태를 보면 Mi A1에 비해 25% 정도 더 남아있다. 성능이 훨씬 더 빠릿한 폰이라는 걸 생각하면 넘어 오길 잘했다 싶다.

노치 디스플레이라서 거슬릴 줄 알았는데 생각보다 거슬리지는 않다. 다른 사람들은 얼마나 알림 종류가 많으면 …으로 압축 되는 것을 거슬려하는지 모르겠지만 내가 받는 알림이라고 해봤자 카카오톡과 트위터, 이메일 알림 정도고 가끔 플레이스토어 업데이트, 은행 거래 내역 알림 오는 게 전부고 이 모든게 같은 시간대에 오는 것도 아니라서.

다만 노치 디스플레이가 거슬릴 때가 있는데, 첫째로 시간 표시가 오른쪽에 표시되기에는 부족한 공간 때문에 왼쪽에 있어서 시간을 보기 위해 습관적으로 오른쪽을 봤다가 없어서 왼쪽을 보게 되는 점이 첫 번째, 상태바가 표시되다가 표시되지 않도록 바뀌는 앱(트위터에서 사진 확대했을 때, 다음 웹툰 스크롤 내릴 때 등)에서 컨텐츠 위치가 이상하게 조정되는 점이 두 번째이다.

붐박스 기능은 제대로 쓰진 않았지만 괜찮다고 생각되는 점은 소리 증폭이 가능하다는 점. 부붐부붐 하는 진동이 스피커에서 소리가 나는 동안에는 폰 뒷면에서 계속 울리기 때문에 소리를 증폭하는 것이 불가능한 손에서도 손맛을 느낄 수 있다는 점도 개인적으로 마음에 듬.

세로 해상도가 길기 때문에 일부 앱(일단 생각나는건 위비 시리즈 앱들)에서 화면 아래쪽이 검게만 보이는 문제점도 있다. 늘려서 보이게 할 수는 있고, 그렇게 며칠 썼는데 어제 보니까 원래대로 돌아와서…

사은품으로 여러 가지를 1,000원에 받았는데 그 중에서 가장 마음에 드는 건 무선충전기. 밤에 잠 자기 전에 충전이 필요할 때 굳이 선 찾아서 폰에 꽂지 않고 그냥 무선충전기 위에 올리면 되니까 편하긴 하다. 다만 충전하면서 폰을 쓰기는 불편하기 때문에 그럴 때는 그냥 선을 꽂는 수 밖에.

개인적으로 왼쪽 중간에 있는 버튼은 없어졌으면 좋겠음. Q보이스든 구글 어시스턴트든 안 쓰는데 버튼이 굳이 필요하진 않잖아. 물론 그러면 제품명 뒤에 있는 ThinQ가 굳이 붙을 필요는 없겠지만.

 

프로젝트

다람 유튜브 업로더 업데이트 근황 – 1

여러 항목 데이터를 동시에 편집할 수 있게 해달라는 요청이 있어서 어떻게 고쳐야 할까 고민하던 중에 이왕 이렇게 된거 싹 다 뜯어고치자 해서 UI부터 전부 뜯어 고치는 중. 거의 새로 만드는 거라고 해도 과언이 아니긴 하지만 기존 코드도 상당수 재활용한다.

이 슬라이드 쇼에는 JavaScript가 필요합니다.

실제 사용이 가능할 정도로 완성해서 배포할 수 있는 최소 예상일은 1개월 후, 아마 중간중간 일이 생겨서 늦어지면 3개월까지는 걸릴 듯. 어디까지나 최소이고, 구상하고 있는 몇 가지 기능을 더 구현하다보면 몇 개월 더 쓸 듯.

정보

오디오 프로그래밍에 대해서 – 4

오디오 플레이어, 오디오 디코더 구현해보면서 공부한 것 정리.

1. 오디오 데이터의 형태

오디오 데이터는 PCM 또는 IEEE 754 부동소수점 등의 방식으로 저장할 수 있다. 이 데이터를 샘플이라고 부르며, 아날로그 오디오를 디지털화 하는 것을 샘플링이라고 한다.

샘플의 데이터 크기를 결정짓는 것은 채널 갯수(Channels), 샘플당 비트 수(Bits per Sample), 샘플링 주기(Sample Rate 또는 Samples per Second)의 세 가지인데, 한 채널에는 1초 길이당 샘플링 주기 크기만큼의 데이터가 만들어진다. 이 샘플 데이터의 총 바이트 길이는 샘플링 주기 * ( 샘플당 비트 수 / 2 )이고, 1초 길이의 총 샘플의 바이트 크기는 결국 채널 갯수 * (샘플당 비트 수 / 8 ) * 샘플링 주기가 된다.

각 샘플은 채널에 따라 따로 존재하는 것이 아니라 번갈아 가면서 있는데, 이 때문에 한 샘플 블럭 정렬(Block Alignment)의 크기는 ( 샘플당 비트 수 / 8 ) * 채널 갯수가 된다(단, MP3 파일은 채널에 따라 데이터를 따로 보관하고 있어 디코딩할 때 합쳐야 한다).

PCM 샘플 데이터를 IEEE 754 부동소수점 샘플 데이터로 변환하는 방법은 간단한데, 각각의 샘플을 2^샘플당 비트 수-1한 크기로 나눈 부동소수점 숫자가 샘플이 된다. 반대로 IEEE 754 부동소수점 샘플 데이터를 PCM 샘플 데이터로 변환하려면 각각의 샘플을 2^샘플당 비트 수-1한 크기로 곱하면 된다.

샘플당 비트 수는 8, 16, 24, 32가 허용된다. 일반적으로 MP3, Ogg Vorbis, AAC 등의 음원은 16비트를 사용하지만 WAV, FLAC 등의 음원은 다른 비트 수도 사용할 수 있다.

어떤 음원이 2채널, 16비트, 44100Hz 샘플링 주기를 가지고, 총 3분 20초 길이라면 이 음원의 블록 크기는 2 * (16 / 8 ) = 4바이트, 초당 바이트 길이(Bytes per Second 또는 Byte Rate)는 2 * (16 / 8 ) * 44100 = 176400바이트,  PCM 총 길이는 176400 * 200 = 35280000바이트 = 약 33.64MB가 된다.

1-1. WAVEFORMATEX

위에서 말한 채널 갯수, 샘플당 비트 수, 샘플링 주기, 블럭 정렬, 초당 바이트 길이, 그리고 각 샘플의 형태를 담는 구조체가 Windows API에서 기본 제공되는데, 이 구조체의 이름은 WAVEFORMATEX이다.

typedef struct {
WORD  wFormatTag;
WORD  nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD  nBlockAlign;
WORD  wBitsPerSample;
WORD  cbSize;
} WAVEFORMATEX;

wFormatTag에는 이 오디오 데이터가 PCM 데이터냐 IEEE 부동소수점이냐를 담고 있는데, 이 구조체가 확장 구조체인가를 담기도 하며, 이 경우 PCM 또는 IEE 부동소수점 데이터인지에 대한 정보는 해당당 확장 구조체를 뜯어야 알 수 있다.

그 외에 nChannels = 채널 갯수, nSamplesPerSec = 샘플링 주기, nAvgBytesPerSec = 초당 바이트 길이, nBlockAlign = 블럭 정렬, wBitsPerSample = 샘플당 비트 수, cbSize = 구조체 총 크기를 뜻한다.

이 구조체가 확장 구조체인 경우 WAVEFORMATEXTENSIBLE로 변환하면 뜯어볼 수 있다. 단, WAVEFORMATEX 구조체 변수가 포인터 변수였을 때에 한정하여 변환할 수 있다.

typedef struct WAVEFORMATEXTENSIBLE {
WAVEFORMATEX Format;
union {
WORD wValidBitsPerSample;
WORD wSamplesPerBlock;
WORD wReserved;
} Samples;
DWORD        dwChannelMask;
GUID         SubFormat;
}  *PWAVEFORMATEXTENSIBLE;

이 구조체 정보에는 위의 WAVEFORMATEX 구조체와 함께 채널 마스크와 서브 포맷을 담고 있는데, 채널 마스크는 멀티채널 오디오일 때 각 채널의 출력 스피커 번호를 명시할 수 있는 플래그를 담는다. 서브 포맷은 이 오디오가 PCM이냐 IEEE 부동소수점이냐를 담고 있다.

 

2. 오디오 코덱과 컨테이너

샘플링 주기가 크면 클 수록, 그리고 샘플당 비트 수가 크면 클 수록 정밀도가 높은 오디오 음원을 보관할 수 있기 때문에 일반적으로 16비트에 44100Hz 주기 이상을 사용하고 있는데, 이 경우 3~5분 내외의 음원을 보관할 때 매우 큰 파일이 생성된다.

이러한 큰 파일을 좀 더 작게 보관하기 위해 인코더를 이용해 압축하는데, 이 때 데이터 처리 방식에 따라 비손실 압축 코덱과 손실 압축 방식으로 나뉘어진다.

비손실 압축은 원본 데이터를 모두 살려서 보관하는 대신 압축률이 작다. 경우에 따라 음원을 압축하지 않은 WAV 파일과 비교했을 때 음원에 따라 무의미한 크기로 압축되기도 한다.

손실 압축은 시간 도메인의 데이터(PCM 또는 IEEE 754 부동소수점 샘플 데이터)를 푸리에 변환(DFT)이나 이산 코사인 변환(DCT) 등을 통해 주파수 도메인 데이터로 변환 후 필요 없는 주파수 대역의 데이터를 양자화를 이용해 모두 삭제하여 남은 데이터를 비손실 압축 방식으로 압축한다. 보통 이 주파수 대역을 결정하는 데에는 비트레이트를 이용하는데, 비트레이트가 작으면 작을 수록 손실되는 주파수 대역도 많아진다.

압축이 됐든 되지 않았든 이러한 오디오 데이터를 그냥 나열한다고 파일이 만들어지는 것은 아니고, 컨테이너 포맷을 이용해 재구성을 해야 하는데, 일반적으로는 AVI 또는 WAV에 사용되는 RIFF 컨테이너, MP4 또는 3GP 등에 사용되는 ISOBMFF, MKA 또는 WEBM 등에 사용되는 Matroska, Vorbis 또는 Opus 등에 사용되는 Ogg 등이 주로 사용된다.

MP3는 컨테이너가 따로 사용되지 않는다. 각 블록을 그냥 시간 순서대로 나열해놓은 원시적인 형태이지만 이 덕분에 ID3 태그 등의 비표준 블록을 추가하더라도 큰 문제가 없다. 또한 각 블럭의 초기 4바이트가 해당 블럭의 오디오 정보를 모두 담고 있기 때문에 따로 컨테이너가 필요 없기도 하다. 물론 MP3 데이터를 MP4 ISOBMFF 등의 다른 컨테이너에 보관하는 것도 가능하다.

 

3. 오디오 API

오디오 데이터를 스피커나 헤드폰 등으로 출력하려면 오디오 API를 사용해야 한다. 보통 이런 API들은 HAL(Hardware Abstraction Layer) 구조를 이용해 HAL의 하위 레이어인 드라이버만 잘 구현하면 이용할 수 있도록 되어 있다.

오디오 API의 종류로는 Windows Audio Session API(WASAPI), DirectSound 8, XAudio 2, OpenAL 등이 존재한다. 모두 저수준 API이며, 때문에 디코딩을 알아서 해주진 않기 때문에 압축된 데이터는 디코더를 이용해 PCM 또는 IEEE 754 부동소수점 데이터로 변환해주어야 한다.

물론 압축을 해제하면 MB 단위의 큰 데이터가 나오기 때문에 메모리를 아끼기 위해 조금씩 쪼개서 출력하기도 하는데 이런 방식을 스트리밍이라고 한다. 메모리 관리 문제도 있지만 사실 디코딩 과정이 오래 걸리는 문제도 있어서 초기 재생 딜레이를 줄이기 위한 방법이기도 하다.

이런 복잡한 구조를 쉽게 사용할 수 있도록 만들어진 고수준 API도 존재하는데, 일반적으로는 FMOD, BASS, NAudio, CSCore 등이 사용된다. 이런 API들은 파일명 입력해주고 재생 함수만 딱 호출해주면 알아서 재생해준다. 다만 API에 따라 다른 파일 포맷은 지원을 못하는 경우도 있음.

잡담

오디오 프로그래밍에 대해서 – 3

첫 번째 만든 라이브러리가 C# 브릿지가 제대로 안 만들어지고 디버깅도 어려워서 두 번째로 만들기 시작했던 순수 C#으로 만들던 라이브러리는 COM 임포트 및 DLL 임포트가 제대로 동작하지 못하고 오작동하는데 디버깅은 더 어려워서 깔끔하게 포기했다.

세 번째로 만들고 있는 라이브러리는 C# 브릿지도 좀 더 쉽게 할 수 있게 외부 노출되는 API는 C 계열로 보이도록 만들고 있다. 구조체에 몇몇 기능들을 함수포인터를 이용하도록 어떤 데이터를 구성하면 C#쪽에서 상속을 이용해 구현할 때도 더 편할 수 있을테니 그 편이 낫지 않을까 싶기도 하고.

뭐 일단 기본적인 기능들은 구현했는데 필터와 관련해서 몇 가지 문제가 있다.

  1. 푸리에 변환을 위해 사용한 라이브러리인 FFTW 3가 GPL 라이센스를 이용해 배포하고 있기 때문에 이를 사용한 라이브러리를 재배포하면 이 라이브러리를 사용한 프로그램도 GPL 라이센스로 배포해야 함.
  2. 이퀄라이저를 구현했는데 어떤 값을 넣어도 약간 깨지는 소리가 미세하게 들리는데 값에 따라서 다른 차이점도 별로 느껴지지 않음.

그래서 지금 FFTW 3를 대체할 가볍고 쉬운 FFT 라이브러리를 찾고 있다. 이퀄라이저도 만만치 않은 문제이지만 더 쉬운 것부터 고치는게 낫지 않을까 싶어서. 거기에 추상화된 필터 오디오 스트림만 제공된다면 이퀄라이저는 사용자가 알아서 구현할 수도 있고.

일단 이 문제들이 해결되면 C# 브릿지를 만들어서 C#에서 테스트 해봐야지.

잡담

오디오 프로그래밍에 대해서 – 2

libFLAC, libvorbis, opus, mp3lime 등을 이용한 디코더도 만들긴 했는데 C++ DLL을 C#에서 쓸 수 있는 브릿지 라이브러리를 만드는 과정에서 디버깅이 너무 힘들어서 일단 중지. 마샬링 과정에서 자꾸 뭐가 잘 안 돼서 삽질도 좀 했고 다행히 미디어 파운데이션 디코더와 WASAPI 오디오 플레이어 브릿지까지는 됐는데 지금은 XAudio 2 오디오 플레이어를 비롯해서 브릿지 이곳저곳에서 오류가 발생하는데 이걸 잡을 방법을 모르겠어서.

그래서 한번 C#만 가지고 프로그램을 만드는걸 도전해보기로 했다. 이미 NAudio나 CSCore같은 라이브러리가 이미 있지만 좀 더 사용법이 쉽고 간단하고 가벼운걸 만들고 싶었다.

일단 미디어 파운데이션 COM 임포트 구문을 추가해보고 있고 테스트 결과 잘 된다 싶으면 WASAPI의 COM 임포트도 추가할 예정. XAudio 2는 C#에서 COM 임포트를 하려면 가상함수 테이블을 만져야 하기 때문에 좀 귀찮아서 구현 예정은 없고 아마 OpenAL을 구현할 수는 있지 않을까 싶다.

뭐 그건 그렇고 mp3lime으로 MP3 파일 디코더 만드는 데는 삽질을 좀 했다. mp3lime으로 디코딩할 때는 ID3 태그를 제외해줘야 되는데다 프레임 크기를 잘못 잡아 읽어 전달하면 샘플이 몇몇 군데 빠져버려서 아예 그냥 직접 MP3 프레임 정보를 읽어들어서 프레임 크기 알아온 다음 프레임 크기만큼 읽어서 mp3lime에 넘겨주는 방식으로 구현해버렸다. ID3 태그 나오면 스킵해버리고.

MP3 전체 길이를 구해야 해서 프레임 당 크기를 검색해봤더니 2.6ms라고 그래서 그거만 가지고 총 프레임 갯수 * 26ms 했더니 실제 길이가 안 나오길래 그것도 좀 삽질했더니 대략 26.125ms 정도였다.

암튼 MP3 디코더 구현이 제일 지옥같았음. 이제 MP4 컨테이너에 AAC 디코더 구현할 차례였는데 얘가 더 지옥같을거 같아서 C++쪽은 일단 놔두고 C# 브릿지 만들기로 했음. 그것마저 지금 터져있는 상태지만.

잡담

오디오 프로그래밍에 대해서 – 1

요즘 자체 오디오 플레이어 및 휴대기기 음악 파일 싱크를 위해서 앱을 만들어보려고 삽질을 좀 했다.

어차피 WPF로 프론트엔드를 개발할 거, NAudio나 CSCore같은 라이브러리를 사용할까 했는데 결국 좀 더 다양한 파일 포맷을 재생할 수 있으려면 네이티브로 개발한 후에 C#용 래퍼를 만드는게 답인 것 같아서 C++로 라이브러리를 만들어보기로 했다.

Media Foundation이야 뭐 원래 사용해왔던 API고, XAudio 2도 사용을 안 해본 API는 아니기 때문에 이 API들로 기본적인 인터페이스는 만들었는데, 여기에 WASAPI(Windows Audio Session API) 재생 기능도 만들어보려고 했는데 의외의 복병이 바로 WASAPI였다.

WASAPI는 독점모드 재생이 가능하기 때문에 오디오 믹싱 처리를 안 하고 바로 원음을 재생할 수 있다는 장점이 있지만 문제가 있다. 바로 리샘플링 기능을 제공하지 않는다는 점.

리샘플링이 지원되지 않으면 무조건 시스템 재생 장치의 설정을 그대로 따라가는 음원만 재생이 가능하다. 예를 들면, 시스템 재생 장치의 샘플 속도 및 비트 수준 설정이 16비트에 48000Hz인 경우, 재생 가능한 음원은 무조건 16비트에 48000Hz 음원만 재생이 가능한 것이다. 이 경우 16비트에 44100Hz 음원은 재생이 불가능하다.

그렇다면 WASAPI로 재생하는 음악 재생 프로그램들은 어떻게 음원을 재생할 수 있는가 하면 프로그래머가 리샘플링을 알아서 한다. Windows에서는 이 리샘플링을 위한 API로 DMO(DirectX Media Object)를 제공한다. WASAPI와 DMO 모두 Windows 기본 API라는 것을 생각한다면 굳이 WASAPI에서 리샘플링을 기본 제공하지 않을 이유가 없었다는 것을 알 수 있겠지. 이건 마이크로소프트의 설계상 문제였다고 생각한다. 하나만 생각하고 둘은 생각 안 한 그런.

DMO로 리샘플링하는 예제도 찾질 못해서 진짜 겨우겨우 구현했다. NAudio랑 CSCore에 구현된 DMO 리샘플러 소스코드를 뜯어보고 거기에서 사용한 인터페이스의 UUID값 찾느라 너무 힘들었음. 결국 정답은 MSDN에 있긴 했지만 그 방대한 문서들 사이에 해당 문서로 가는 링크를 못찾아서 문제였다.

이제 OpenAL 및 DirectSound 오디오 플레이어도 구현하고 LimeMP3나 FAAD2, libogg/libvorbis/libFLAC/opus를 이용해서 오디오 디코더도 구현해야 되는데 너무 힘들다. 일단 좀 쉬어야지.

게임 리뷰

[리뷰] 더 라스트 오브 어스

드디어 밀리고 밀린 게임 중 하나인 더 라스트 오브 어스 엔딩을 봤다.

The-Last-of-Us-Remastered

초반엔 좀 루즈했는데 갈 수록 흥미진진해져서 좋았다. 그 반대였다면 엔딩을 보는데 더 힘들었을 것 같았다.

총알이 너무 부족했다. 쉬움 난이도로 했는데도 총알이 생각보다 안 나오는데다 막상 총알 안 쓰고 근접 공격 위주로 할 때는 총알이 보여도 갯수 한도 때문에 줍줍하질 못해서 슬펐음 ㅇㅅㅠ 앨런 웨이크도 총알 줍기 어려웠는데 얜 더 어려웠다.

길 찾기가 살짝 힘들었다. 보통 생존 게임, 공포 게임은 빛의 강도로 길을 알려주는데 라오어는 그런 부분에서 좀 부족하다고 생각이 들었음. 퍼즐도 감이 안 오는데 하다보는 되는 경우도 있었고.

남들처럼 이 게임 강력 추천! 이런 것은 개인적으로 아니다. 생각보다 버그도 많고, 길 찾기도 어렵고, 전투도 난이도가 꽤 된다는 점에서 좀 감점. 스토리도 중요도가 높지만 게임은 사용자와의 상호작용도 중요하다는 점에서 전투 난이도가 높다는 점은 개인적으로 불호하는 편이라서.