프로젝트

다람 유튜브 업로더 업데이트 근황 – 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

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

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

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

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

기기 리뷰

[리뷰] 샤오미 Mi A1

LG 넥서스 5X를 비롯해서 퀄컴 스냅드래곤 808 시리즈를 사용하는 기기들이 가지고 있다는 고질적인 문제점. 사용하다 보면 냉납으로 의심되는 증상으로 인한 무한 부팅 현상이 발생해서 A/S를 맡겼다. 다행히 넥서스 5X의 무한 부팅 현상은 메인보드 무료 교체 대상이라 서비스는 잘 받았지만 그래도 한번 무한 부팅을 겪어 보니 더는 메인기기로 사용하기 어렵다고 생각해서 대체할 기기를 찾던 중 안드로이드원을 OS로 사용하는 샤오미 Mi A1이 마음에 들었다.

thumb_85857_default_big

1주일 정도 써본 결과, 일반적으로 사용하기에 무리가 없었다. 기능도 충분했고.

디자인이 사진으로 보는 것보다 더 아이폰스럽다. 이건 개인적으로 감점. 난 그래도 실물 보면 괜찮을 줄 알았는데 실물이 더 아이폰 닮았음. 좀 샤오미면 샤오미의 디자인을 잘 찾아서 적용했으면 좋겠다는 생각이다.

카메라는 별로 쓸 일이 없어서 잘 모르겠지만 넥서스 5X랑 비교해서 모자라지는 않는 듯. 애초에 카메라 성능 보고 산 것도 아니고, 넥서스 5X에 흔들림 보정 없었는데 얘도 없고.

프로세서 성능은 스냅드래곤 808과 비교해서 벤치마크 상으로는 큰 차이가 없다고 들었는데 실제로 사용하면 버벅거리는 경우가 종종 있다. 그 버벅임을 램 용량빨로 커버해서 그나마 넥서스 5X에 비해서 낫다고 체감되는 편.

휴대폰 케이스에 신경을 쓰고 있다면 Mi A1은 비추. 국내 마켓을 비롯해서 미국 아마존 등에도 괜찮은 케이스 찾기가 어렵다. 애초에 다양성이 많이 부족함. 이건 화면 보호 필름도 마찬가지다. 물론 나처럼 넥서스 5X같은 비주류 폰을 쓰다가 넘어왔다면 이 점은 크게 불편하지 않을지도.

소리랑 화면 밝기 조절은 대체 왜 있는걸까 싶을 정도로 임계값이 극단적이다. 소리는 최대한 낮춰도 매우 크고, 밝기 조절은 중간 밝기가 너무 밝아서 1/4 정도로 낮추면 너무 어둡고… 자동 밝기 조절을 켜놨는데 결국 수동 조절하고 있음. 벨소리는 걍 진동 모드 해놨고.

무게는 넥서스 5X에 비해서 더 나가는데, 아무래도 뒷면 알루미늄 때문인 것 같음. 애초에 크기도 0.2인치 정도 더 크고.

카툭튀가 있다. 그것도 넥5X처럼 가운데 부분인 것도 아니고 정면을 기준으로 오른쪽에 치우쳐 있어서 케이스 없이 바닥에 놓고 써야 한다면 더 불편함.

배터리는 넥서스 5X에 비해 300mAh 정도 더 많은데, 프로세서 전력 소비량이 더 적어서 그런건지 배터리 용량 차이에 비해서 더 오래가는 편이다. 그런 의미에서 발열도 넥5X에 비해 훨씬 덜 뜨겁고.

하단의 하드웨어 버튼 배열이 너무 마음에 안 든다. 삼성 갤럭시 시리즈를 사용하다가 넘어간 사람이라면 좋아라 하겠지만 표준 배열(뒤로 가기, 홈, 태스크 버튼 순서)만 써왔던 나로써는 너무 불편함. 앱을 이용해서 순서를 바꿔놨는데 문제는 안드로이드 8.0부터 이런 백그라운드 동작 앱이 알림 목록에 무조건 표시된다는 점. 안드로이드원이라고 하는 일종의 표준 OS를 적용한 폰인데 정작 버튼 배열이 표준이 아니라니.

결론을 내리자면 종합적인 점수는 개인적으로 그냥 쏘쏘. 바로 전에 사용하던 넥서스 5X 자체가 일반적으로 사용하기에 무리가 없었고 게임도 잘 돌렸던 폰인데 넥5X랑 성능상 큰 차이가 없는데(다만 램이랑 내장메모리는 넥5X의 두 배) 발열도 적으니까. 대신 불편한 점도 있고, 소리랑 화면 밝기 임계값 문제도 있고. 20만원~30만원 사이 가격(나는 기기값 21.5만원 + 배송비 1.5만원에 홍콩 직구 했음)을 지불하고 쓴다면 이런 불편함은 다 감안하고 쓸 수 있다고 본다. 넥서스 5X는 80만원 가까이 했으니…

잡담

다람 리네이머의 스크립트 기능

다람 리네이머에서 개인적으로 별로 쓸모는 없지만 혹시나 필요한 사람이 있을 것 같아서 계속 개발을 시도했던 기능 중 하나가 스크립트를 이용한 일괄 처리 기능이다. 버전 2.x대에서 시험적인 스크립트 기능을 만든 적이 있지만 문법 자체가 너무 제한적이라서 버전 3.x대 올라오면서 그냥 버렸다.

대신 3.x에서 좀 더 유연한 스크립트를 만들기 위해서 이런저런 시도를 많이 했었는데, 그러느라 기능 추가나 버그 수정이 더뎌지기도 했다.

결국은 자체 개발 스크립트 언어가 아닌 기존 스크립트 언어를 사용하기로 하고 여러 시도를 해봤는데, C#, IronPython, IronRuby, Powershell, Javascript를 고려해봤다.

C#은 완전형 객체지향 언어이기 때문에 스크립트로 사용하기엔 다소 어려움이 있었다. 네임스페이스도 지정해주고 클래스도 지정해주고 메인 함수도 지정해줘야 해서 초보자가 사용하기엔 어렵지 않을까 하는 생각이 들었다.

IronRuby와 IronPython은 버전업이 중단된지 최소 2년은 됐기 때문에 제외했다. 특히 IronRuby는 버전업 중단이 꽤 오래 돼서 장기적으로 봤을 때 문제가 생길 가능성이 크다고 생각했다. 버전업이 중단되지 않았다면 IronPython에 손을 더 들어줬을 것 같다.

Powershell도 문법적으로 괜찮다고 생각했었는데 C#과 연동해서 내 자체 명령어를 추가할 방법이 없었다.

결국 Jint를 이용해 Javascript로 스크립트를 구현했다. C#과 연동해서 객체나 함수를 추가해줄 수도 있고, 스크립트 결과를 받아올 수도 있어서 편리했다. Jint 자체적으로 CLR 연동도 가능은 하지만 보안 문제로 일단 보류했다. 만약 추가했다면 .NET 프레임워크 기능도 사용할 수 있으니 더 편리할 수는 있었을 듯.

아무튼 3.3버전에서는 좀 더 테스트 후에 다시 일괄 처리 기능이 추가될 예정이다. 거기에 더불어서 몇 가지 버그도 수정하고 싶고.

잡담

안드로이드 SDK 못 써먹겠다

개인적으로 싫어하는 개발 환경이 몇 가지 있는데, 그 중 하나가 자바고, 다른 하나가 안드로이드 SDK다. 물론 안드로이드 SDK가 자바에 종속되어 있다는 것을 생각하면 안드로이드 SDK를 싫어하게 되는 데에는 이유가 있었겠지만.

저번주 내내 삽질해서 엄청 심플하게 정부청사 식당 식단표 뷰어를 만들었는데, 예전처럼 윈도우폰을 쓰고 있었으면 하루 정도면 금방 만들어놓고 써먹었을 텐데 안드로이드용으로 개발하려다보니 API가 너무 답답해서 이렇게 만들어보고 실패, 저렇게 만들어보고 실패 해서 그냥 빈 액티비티에 대충 때려넣어서 만들었다. 그래서 지금 UI가 똥망인 상태.

언어 자체는 코틀린이 나오고 있지만 얘도 별로 마음에 안 들고, 안드로이드 SDK 구조 자체도 솔직히 별로 마음에 안 듦. API가 직관적이지 않아서 이리저리 삽질하고 나서야 기능 구현이 되는 모양새라서 그런 느낌. 하다보면 결과물이 나오긴 나오는데 만족스럽진 않고 위태위태한 그런…?

XAML + C#으로 구현해본 사람들은 다 안다(WPF, Silverlight, UWP, …). 이게 얼마나 편하고 직관적인지. 그리고 이 XAML + C#으로 구현하려고 Xamarin Forms 써서 만들려고 했더니 안드로이드 네이티브 라이브러리는 아니라서 그런건지 자료도 적고 겨우겨우 핀치 투 줌 기능 하나 구현했더니 제대로 줌도 안 됨. 걍 Xamarin Droid로 개발할까 했는데 그거랑 Android Studio + 자바로 개발하는거랑 별 차이도 없어서 걍 오랜만에 자바나 써볼겸 자바로 구현해서 스토어에 제출. 근데 걍 C#으로 개발하는게 더 낫지 않았을까 싶을 정도로 안 익숙했다. 다음엔 걍 Xamarin Droid + C#으로 개발해야지.

뭐 암튼 안드로이드 API 전체적으로 개선 좀 해줬으면 좋겠다는 생각이다. 불가능하겠지만.

게임 리뷰

[리뷰] 언틸 던

일요일에 플레이스테이션 4로 DJMAX 리스펙트를 하려다가 동생이 언틸 던으로 BD를 교체해놨길래 사놓고 묵혀두기도 좀 그래서 시작해봤었다.

Until_Dawn_cover_art

플레이스테이션 4로 즐길 수 있는 공포 게임으로는 꽤 흥미로워 보여서 구매했었는데 정작 쫄보라서 일단 묵혀두고 있었는데 플레이 하고 나니 플레이하기 잘했다고 생각한다.

일단 아웃라스트나 화이트데이같은 1인칭 공포 게임은 아니다. 그것만으로도 점수를 후하게 줄 생각이 있다.

대부분 공포게임 하면 사실감있게 표현해야 한다고들 생각하는 것 같을 정도로 최근에 1인칭 공포 게임이 많이 나왔었는데 언틸 던은 다행히 3인칭 시점으로 진행되는 게임이고, 더군다나 QTE를 제외하면 사용자가 급박한 상황에 취해야 할 행동도 적은 편.

그럼에도 불구하고 게임을 처음하는 사람은 꽤 공포감을 가지고 플레이를 해야 할 정도로 분위기가 무섭다.

개인적으로 라이프 이즈 스트레인지의 선택지가 생각보다 분기를 만들어주지 못하는 점이 아쉬웠는데 언틸 던은 그런 면에서는 좀 더 좋았다. 내 선택 하나하나가 인물의 구성이나 엔딩을 바꿀 수 있었기 때문(LIS가 그렇지 않는 다는 것은 아니지만 인물의 다양성이 좀 더 커지는 점에서 언틸던은 좀 더 낫다).

다만 스토리를 대충이라도 알게 되면 공포감이 많이 덜해진다는 점은 아쉽다. 2회차를 플레이해도 좋은 구성이었으면 더 좋았을텐데.