잡담

CMake 사용기

여태 엔진 만들던거 Visual Studio C++ 프로젝트로 생성해서 쓰고 있었는데 다른 플랫폼용으로도 쉽게 개발하려면 Makefile이 좀 더 낫지 않을까 싶어서 VS프로젝트를 벗어나볼까 싶어졌다. 하지만 Makefile을 그대로 쓰면 귀찮기 때문에 CMake를 쓰기로.

개발은 그대로 Visual Studio로 하지만 폴더를 읽어오면 CMake를 쓸 수 있고 리눅스나 맥에 가서는 Visual Studio Code 같은 걸로도 개발할 수 있으니까 더 낫다는 생각도 들고.

문제는 cpp 파일 하나 하나 추가할 때마다 CMakeLists.txt 파일을 수정해줘야 된다는 점인데 이거 어떻게 와일드카드 쓸 수 없나 싶기도 하고.

그간 Visual Studio 컴파일러 설정이 얼마나 쉬웠는지 깨닫게 되는 부분이… CMake 쓰니까 각 컴파일러마다 설정 직접 다 지정해줘야 돼서 귀찮아 :Q…

광고
잡담

libWebP와 mozjpeg, Zopfli 삽질기

오랜만의 프로그래밍 일기. 요즘엔 이미지를 압축하는 프로그램을 만들어보고 있다. 프로그램 이름은 Degra. 열화 또는 저하를 뜻하는 degrade에서 따온 이름이다. 그러니까, 손실 압축 위주로 압축하는 프로그램.

UWP를 타겟으로 만들기 시작했는데, GUI 프레임워크 중에 쓸만하다고 생각하는건 XAML+C# 조합이고 x86, x64, ARM, ARM64 모두 동시 빌드해서 배포 가능한 플랫폼은 Win32 데스크톱보다는 UWP니까.

그런데 막상 UWP 앱으로 개발하려고 보니까 mozjpeg가 NASM이나 YASM을 써야 빌드가 되는 구조라 이걸 어쩌지 싶었다. 그래서 일단 libWebP부터 붙여서 webp 파일으로 압축하는 기능부터 구현.

멀티스레드 인코딩 기능이 분명 있는데 아무리 돌려도 WebP 인코딩에 싱글코어만  쓰는 문제가 있어서 아니 도대체 왜 이러는 걸까 했는데 릴리즈 모드로 빌드해보니 정상 작동. 아무래도 디버그 편의성을 위해서 디버그 모드에서는 싱글코어만 쓰는 걸로 추정된다. 어쨌든 WebP 인코딩 기능은 의외로 매우 편했다.

WebP 인코딩 기능 붙여놓고는 다시 mozjpeg를 붙이는 작업으로 돌아왔다. 일단 CMake로 Visual C++ 프로젝트 생성해놓고 빌드 결과물인 정적 라이브러리 파일을 UWP에 붙여보니 생각과 달리 잘 붙었다. x64로 테스트를 했었으니 나머지 x86, ARM, ARM64도 프로젝트 생성해서 붙이려고 하니 윈도우용 ARM과 ARM64를 mozjpeg에서 지원하지 않는 또다른 문제점이 있었다. 찾아보니 이슈는 만들어져 있는데 테스트 기기가 없어서 이슈가 닫혀있었다. 나도 뭐 딱히 테스트할 기기는 없고 해서 일단은 x64와 x86용만 붙여놨다. 인코딩 기능 인터페이스 자체는 libjpeg-turbo의 것을 사용하면 해결.

PNG 압축은 좀 고민을 더 했다. 8비트 인덱스 픽셀 포맷으로 만드는 기능은 추가가 이미 돼 있었고 인코딩은 Windows Imaging Codec(WIC)을 이용했는데 생각보다 압축률이 엄청 낮지는 않았음. 찾아보니 OptiPNG, pngquant, ZopfliPNG 등이 있어서 적용을 시도.

OptiPNG는 벤치마크 자료를 찾아보니 ZopfliPNG보다 성능이 딸리는데다 벤치마크 자료 찾아보기 전에 빌드 시도할 때 난황이 있었어서 포기. pngquant는 GPLv3 라이센스라 가져다 쓰기 좀 그랬는데 심지어 찾아보니 그냥 8비트 인덱스 픽셀 포맷으로 만들어주는 기능(+디더링, 미디언컷)만 있는 것 같길래 일단은 도입 포기. Zopfli만 적용하기로 했다.

드라마틱하게 압축률을 높여주진 않아서 실망했다. ZopfliPNG도 결국은 개선된 Deflate 알고리즘을 쓰는터라. Inflate에 들어가는 시간이 그냥 단순 WIC만 쓰는 것보다 많이 길어서 일단은 옵션으로만 남겨뒀다. 여러 인자를 좀 더 만져봐도 속도는 느려지는데 압축률은 고만고만 하더라. fast_zlib을 써서 libpng를 바로 적용해보는 것도 고민 중.

그래서 일단은 WebP, JPEG, PNG의 압축 기능은 구현이 됐다. 앱에서 GIF, TIFF 등의 그 외 포맷도 압축 기능을 지원할 지는 테스트 좀 더 해보고. UWP 파일 접근 권한 문제 때문에 안 그래도 골치아파.

기기 리뷰

[리뷰] JONSBO C3 PLUS

책상을 좀 더 넓게 쓰려고 최대한 깊이가 작은 미니타워 케이스를 찾아다녔고 특히나 방 창문쪽에 딱 붙여서 쓰고 싶어서 오른쪽 전면측 면에 바람 구멍이 있는 케이스를 찾다가 딱 마음에 드는 케이스를 찾았다.

BRAVOTECJONSBOC3PLUSSilverMain04

일단 미니타워 주제에 깊이가 360mm라서 다른 미니타워보다 작다. 너비가 200mm로 다른 케이스들에 비해 좀 펑퍼짐한 느낌이지만 나쁘지 않다.

3.5인치 하드디스크 2개, 2.5인치 하드디스크 3개?4개?를 달 수 있다. 각 하드디스크용 고정나사 자체가 모양은 비슷한데 다르니 남는 나사는 달리 보관하는 것이 좋을 듯. 메인보드, 파워, 그래픽카드까지 다 단 후에 하드디스크 고정을 메인보드, 파워서플라이를 장착하기 전에 해야 되는 줄 알고 멘붕했는데 다행히 그건 아니었다. 하드디스크에 나사를 고정하는건 고무 고정대 때문이고 이 고무 고정대를 구멍에 끼워서 밀어넣는 방식. 작은 케이스에 최대한 조립을 쉽게 할 수 있도록 나름 배려한 디자인이다.

에어홀도 다섯 개로, 전면 두 개, 상단 한 개(140mm 팬 기본 제공), 후면 한 개, 하단 한 개이다. 하단 팬이 있어서 그래픽카드 발열 해소에도 나쁘지 않은 듯. 단점이 있다면 팬 소음을 못 잡는다. 전에 쓰던 케이스에 달았던 ARCTIC F12 팬 다섯 개 중 네 개를 이쪽에 옮겨 달았는데 전보다 소음이 심해진 걸 보면 풍절음을 못 잡는 구조가 아닐까 싶다. 전보다 케이스 무게가 무거워진 만큼 진동음은 아닌거 같으니까. 여름까지 써보고 여름에 이 소음에 발열을 제대로 못 잡는다면 그냥 쓰고 발열 잘 잡으면 저항 써서 팬속을 좀 낮춰볼까 함.

문제점이 있다면 겉면은 날카롭지 않은데 안쪽이 살짝 날카로운 부분이 있다. 조립하면서 케이스를 움직일 때 손을 베지 않도록 주의해야 한다. 엄청 날카로운건 아니고 잘못 만지면 베일 거 같은 날카로움이라 안쪽에 손 대고 세게 누르면서 왔다갔다만 안 하면 될 것 같기도.

또 다른 문제점은 선 정리가 어렵다는 점. 하단에서 위쪽으로 올리는 에어홀이 있는데 이 부분이 파워서플라이 케이블들을 밀어넣으면 바람이 잘 안 올라오는 문제가 있어서 선 정리를 빡세게 해야 하는데 내가 보기엔 파워서플라이를 모듈러 방식으로 해서 안 쓰는 선을 최대한 없애야 하지 않을까 싶다. 파워의 SATA 전원 케이블도 ㄱ자 방식은 하드디스크에 장착하기 어렵다는 점.

뒷판 닫기도 좀 어려웠다. 뒷판쪽에 선정리를 하는 것이기 때문에 세워놓은 상태로 닫는건 말도 안 되고 눕혀놓고 꽉 누른 채로 나사를 조여야 한다.

단점이 많아보이지만 디자인이 다 커버하니까 괜춘. 원래는 좀 더 멋있는 블랙 모델을 사고 싶었는데 78,000원에 구매하긴 어렵고 브라보텍 공홈에서 실버 리퍼가 5만원에 나와서 갬맥스400 리퍼랑 같이 산거라… 솔직히 알루미늄 실버는 뭐랄까 대충 만든 느낌? 요즘 왜인지 모르게 유행하는 공사 하다 만 것 같은 카페 같은 느낌?이 들어서.

기기 리뷰

[리뷰] 한성컴퓨터 ULTRON 2559G

그동안 살까말까 고민해왔던 FHD 해상도의 144Hz 모니터를 구매했다. 끊임없이 아 굳이 144Hz 모니터를 사야하나 고민해왔었는데 기존에 쓰던 AOC 2477의 전면 편광필터 프레임이 살짝 뜨길래 홧김에 사버리고 기존 모니터는 동생 줌. 물론 정상 동작하고 있으니 동생도 한동안은 더 쓸 수 있겠지.

6516081_1

그동안 144Hz를 살까 고민하다가도 결국 포기했던 이유는 굳이 144Hz를 써가면서까지 게임을 해야 하나 하는 생각이었는데, 이 모니터를 구매하고 체감하면서 알았다. 게임을 하고 있지 않더라도 눈이 더 편해진다는 점을.

마우스의 움직임은 물론이고 웹 페이지의 스크롤, 애니메이션 등이 더 자연스럽게 보인다. 게임은 말할 것도 없다. 물론 144Hz를 쓴다고 해서 60Hz 대비 게임 실력이 더 늘진 않는다. 실시간 액션 게임이라면 상대방보다 좀 더 빠르게 볼 순 있겠지만 겨우 10ms 더 빨리 본다고 더 빨리 쏠 수 있을까? 라는 생각을 해본다면…

프리싱크도 켜놓긴 했지만 딱히 체감이 되진 않는게, NVIDIA GeForce GTX 970 쓸 때는 오버워치에서 테어링을 자주 경험했는데 AMD Radeon RX 480을 쓰고 부터는 테어링을 본 적이 없어서… 언젠간 체감할 수 있게 되지 않을까 싶긴 하지만 지금은 딱히 체감되진 않는다.

2559G는 TN 패널을 쓰고 있는데, 내가 굳이 더 저렴한 VA 패널 144Hz 모니터를 구매하지 않은 이유는 내 눈이 VA 패널의 잔상을 인식하기 때문이다. 서브 모니터로 쓰고 있는 BenQ GW2470HL이 VA 패널인데 60Hz에서도 잔상이 심해서 이 모니터로는 게임을 못 하고 있기 때문에 VA 144를 구매하지 않고 TN 144를  구매했다.

문제는 TN 패널의 색감이 IPS 패널에 비해 별로라는 점인데, 2559G에서 사용하는 TN 패널은 내가 예전에 써봤던 TN 패널 모니터에 비해 더 쨍한 느낌을 준다. 물론 이 더 쨍한 느낌은 게임에서 색의 대비를 더 잘 느끼게 해주기 때문에 게이밍용으로는 더 좋다고 보인다.

패널 문제와는 별개로 OSD 버튼에 불만이 좀 많다. 버튼 크기가 작은데다가 모니터를 끄기 위해서는 전원 버튼을 두 번 눌러야 하기 때문. 버튼이 작기 때문에 버튼이 어디 있는지 더듬더듬 하기도 쉽지 않다.

가성비 자체는 좋다. FHD 144Hz 모니터가 무결점 기준 25만원. 4년 전에 FHD 60Hz 모니터가 24만원 내외였다는 것을 생각한다면 좋은 가격대라고 보인다. 물론 한성컴퓨터의 A/S를 생각해본다면 제에에발 고장 안 나길 빌어야 한다는 점이 마이너스 요소겠지만 양품만 뽑는다면 전체적으로 좋은 선택이지 않을까.

잡담

[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로 쓰고 싶음

+ (2019. 03. 02.) 결국 샀다. FHD 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에 따라 다른 파일 포맷은 지원을 못하는 경우도 있음.