[Python] MIDI viewer

개인적으로 MIDI 파일을 처리하는 간단한 툴을 만들어야해서, 그 첫번 째 단계로 MIDI 파일을 분석해서 화면에 보여주는 기능을 만들었다.
이 기능은 실제 런타임의 일부가 아니라 전처리를 위한 툴의 형식이었으므로 사용 언어는 Python으로 하였다. 
 
사용자 삽입 이미지
 그림 1. 이 앱을 통해 트랙별 MIDI 출력을 한 예
 
[첨부] Source code (Python)
 

 
이왕 만든 김에 첨부한 소스 코드에 대한 간단한 설명을 덧붙였다.


import midi
import math
import numpy as np
import matplotlib.pyplot as plt
 
MIDI viewer를 만들기 위해 사용하는 모듈들이다.
 
이중에 'midi'는 python-midi인데, 일반적인 방법으로는 3.x에서 설치가 안 되어서
pip install git+https://github.com/vishnubob/python-midi@feature/python3
 
와 같은 방법으로 설치를 하였다.
 
그 이외의 나머지는 수치 계산을 위한 모듈(numpy)과 그래프를 그리기 위한 모듈(matplotlib.pyplot)이다.
 
pattern = midi.read_midifile("somewhere_over_the_rainbow.mid")
pattern.make_ticks_abs()
 
tracks = midi.Track(pattern)
 
여기는 MIDI 파일을 읽어서 분해하는 부분인데, pattern에는 MIDI의 헤더부분과 트랙들으로 나누어 저장된다.
MIDI의 tick(일종의 time-stamp)은 항상 바로 앞의 이벤트와의 상대적인 길이로 저장이 되는데, 여기서는 시간대 별로 이벤트를 나열해야 하므로 tick을 절대적인 값으로 변경을 하였다. 그리고 tracks에는 pattern에 있는 트랙의 집합을 꺼내서 따로 저장을 하였다.
 

resolution = pattern.resolution
bpm = 0
max_channel = -1
beats = 0
beat_type = 0
 
MIDI의 tick의 단위는 resolution에 의해 정의가 된다. 일반적으로는 ticks per quarter-note 라고 하는데 480 또는 960으로 정의할 때가 많다.
그리고 추가로 더 알아내어야 하는 것은 채널의 개수와 몇 분의 몇 박자인지를 파악하는 것인데, 이것은 MIDI 이벤트를 모두 뒤져봐야 알 수 있는 것이라서 아직은 invalid로 정의하였다. 채널의 경우도 트랙과는 다른 개념이어서 모든 MIDI 이벤트를 확인해야지만 어떤 채널이 사용되었는지 알 수 있다.
 
4분의 3박자(3/4) 라면 beats = 3, beat_type = 4 를 의미한다.


for track in tracks:
    for event in track:
        if isinstance(event, midi.events.TimeSignatureEvent):
            # data=[4, 2, 24, 8]
            if beats <= 0:
                beats = event.data[0]
            if beat_type <= 0:
                beat_type = 1 << event.data[1]
 
        if isinstance(event, midi.events.NoteOnEvent):
            if max_channel < event.channel:
                max_channel = event.channel
 
        if isinstance(event, midi.events.SetTempoEvent):
            tempo_in_msec = (event.data[0] & 0xFF<< 16 | (event.data[1] & 0xFF<< 8 | (event.data[2] & 0xFF)
            if tempo_in_msec > 0:
                bpm = 1000000 * 60 // tempo_in_msec
  
여기서는 BPM(Beats Per Minute)과 박자와 제일 큰 채널 번호를 찾는다. (중간에 변경 가능한 값들이지만 그 경우는 고려하지 않는다)
 
전체 MIDI 이벤트에서 박자 변경 이벤트를 찾은 뒤, 그 이벤트의 data에서 data[0]을 beats로, 2^data[1]을 beat_type으로 둔다.
채널의 경우는 최대 값만 찾도록 하였는데, 중간에 쓰이지 않는 채널이나 특정 용도(drum)로 지정된 채널도 있을 수 있다.
마지막으로 템포 변경 이벤트를 찾은 후에 그 값을 읽는데, 가변 길이가 아닌, 24-bit big-endian 형식의 숫자로 읽으면 된다.
 
bpm = bpm if bpm > 0 else 72
beats = beats if beats > 0 else 4
beat_type = beat_type if beat_type > 0 else 4
max_channel = max_channel + 1
 
print("RESOLUTION = {0}".format(resolution))
print("BPM = {0}".format(bpm))
print("MAX CHANNEL = {0}".format(max_channel))
 
여기에는, 해당 MIDI 이벤트가 누락된 경우에는 디폴트 값으로 설정하게 하는 부분을 두었다.
BPM은 72, 박자는 4/4를 디폴트로 하였는데, max_channel의 경우는 현재 발견된 채널 번호 +1 을 하였다. channel 번호는 0번부터 시작하기 때문이다.
 

noduvels = [[] for i in range(max_channel)]
 
for track in tracks:
    incomplete = {}
    incomplete_2nd = {}
 
    for event in track:
        is_note_on = isinstance(event, midi.events.NoteOnEvent) and event.velocity > 0
        is_note_off = isinstance(event, midi.events.NoteOffEvent) or (isinstance(event, midi.events.NoteOnEvent) and event.velocity == 0)
 
        if is_note_on:
            time_stamp = event.tick / resolution / beats
            key = event.channel << 8 | event.pitch
            if not key in incomplete:
                incomplete[key] = [time_stamp, event.velocity]
            elif not key in incomplete_2nd:
                incomplete_2nd[key] = [time_stamp, event.velocity]
            else:
                print("[WARNING] Too many intersections")
 
        if is_note_off:
            time_stamp = event.tick / resolution / beats
            key = event.channel << 8 | event.pitch
            if key in incomplete_2nd:
                noduvels[event.channel].append([event.pitch, incomplete_2nd[key][0], time_stamp, incomplete_2nd[key][1]])
                del incomplete_2nd[key]
            elif key in incomplete:
                noduvels[event.channel].append([event.pitch, incomplete[key][0], time_stamp, incomplete[key][1]])
                del incomplete[key]
            else:
                print("[WARNING] Cannot find NoteOn before NoteOff")
 
    assert len(incomplete_2nd) == 0 and len(incomplete) == 0
 
이전까지가 속성 값을 읽기 위한 작업이었다면, 이번부터는 제대로된 트랙별 note를 읽는 과정이 들어가 있다.
 
주로 확인할 이벤트는 NoteOn과 NoteOff인데, 일부 DAW에서는 NoteOff 대신 NoteOn의 velocity를 0으로 하여 off의 효과를 내는 것도 있기 때문에 NoteOff의 경우는 조금 더 복잡하게 판단을 한다.
 
incomplete 변수를 두는 이유는 아직 NoteOff가 일어나지 않은 note들을 임시로 보관하기 위함인데, 간혹 표준과는 다르게 사람의 실수에 의해 같은 pitch의 note인데도 on과 off가 중첩되는 구간이 생기기도한다. 이런 것을 한 번 정도는 허용해 주기 위해 incomplete_2nd 변수를 추가로 두었다.
 
NoteOff 이벤트가 발생하면 incomplete 에 있는 note를 꺼내어서 완성을 하게 되는데, pitch, 시작 time stamp, 끝 time stamp, velocity를 각 채널별로 저장한다.
(제대로 하려면 expression과 sustain pedal과 pitch bend 등도 저장을 해야 하나, 재생의 목적이 아니므로 생략하였다)


def _getColorList(selected_cmap):
    return [selected_cmap(a) for a in np.linspace(0.0, 1.0, max_channel)]
 
color_list = _getColorList(plt.get_cmap('rainbow'))
 
sub_plt_1 = plt.figure(figsize=(14, 5)).add_subplot(1, 1, 1)
 
max_measure = -1
min_note = 128
max_note = 0
 
for ix_channel in reversed(range(max_channel)):
 
    label_name = 'Track {0:0>2}'.format(ix_channel)
 
    for noduvel in noduvels[ix_channel]:
        note = noduvel[0]
        x_1 = noduvel[1]
        x_2 = noduvel[2]
        vel = noduvel[3]
 
        if max_measure < x_2:
            max_measure = x_2
        if min_note > note:
            min_note = note
        if max_note < note:
            max_note = note
 
        plt.plot([x_1, x_2], [note+0.5, note+0.5], c=color_list[ix_channel], alpha=(vel*0.8/127+0.2), linewidth=2.5, solid_capstyle='butt', label=label_name)
        label_name = None
 
max_measure = math.ceil(max_measure)
 
plt.title("MIDI track")
 
plt.xlabel("measure")
sub_plt_1.set_xticks(np.arange(0, max_measure, 4))
sub_plt_1.set_xticks(np.arange(0, max_measure, 1), minor=True)
 
plt.ylabel("pitch")
sub_plt_1.set_yticks(np.arange(24, 100, 12))
sub_plt_1.set_yticks(np.arange(24, 100, 1), minor=True)
 
sub_plt_1.grid(which='major', alpha=0.5)
sub_plt_1.grid(which='minor', alpha=0.2)
 
handles, labels = sub_plt_1.get_legend_handles_labels()
labels, handles = zip(*sorted(zip(labels, handles), key=lambda t: t[0]))
sub_plt_1.legend(handles, labels, fontsize=8, loc='upper left')
 
plt.show()
 
위의 과정에서 채널별로 저장된 note 정보를 화면에 출력한다.
 
가로는 마디의 단위이고, 세로는 pitch의 단위인데, 옥타브 단위로 구분선을 두었다.
채널별로 색깔을 다르게 하고, velocity별로 alpha 값을 다르게 주었다.
 
matplotlib.pyplot 의 내용은 MIDI와 직접적인 관계는 없으므로 설명은 생략한다.


그리고 그 결과이다.

사용자 삽입 이미지
그림 2. 위의 코드로 트랙별 MIDI 출력을 한 예

Posted by 안영기

2019/07/07 00:38 2019/07/07 00:38
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/55

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

[문제의 발단]
2019년 8월부터 구글 플레이에 올라가는 앱은 64비트를 대응해야 한다고 한다.
 
지금 서비스 하고 있는 앱의 경우, 핵심 모듈은 C++로 되어 있는데 모두 몇 년 전에 만들었던 32비트 so 를 사용하고 있었다.
그래서 예전의 C++ 소스 코드를 꺼내서 다시 64비트 so로 만드는 과정이 필요했다.
 
예전에는 Eclipse에서 ndkBuild로 만들었다면, 지금은 최신 Android Studio에서 새로운 NDK 빌드 방식으로 만들어야 하는 점이 달랐는데,
새로 NDK Makefile을 만드는 것도 우여곡절이 많았지만 그건 어쨌든 과정의 일부였고, 최종적으로는 NDK 빌드를 성공하고 64비트 so를 적용하여 테스트를 거친 뒤 public release를 했다.
 
[문제의 발생]
그런데 릴리즈가 되자마자 며칠만에 에러 보고가 날아오기 시작했는데, 다음과 같은 메시지였다.
 
Fatal Exception: java.lang.UnsatisfiedLinkError
dlopen failed: cannot locate symbol "__register_atfork" referenced by "lib*****.so"...
 
java.lang.Runtime.loadLibrary (Runtime.java:372)
java.lang.System.loadLibrary (System.java:988)
 
JNI 연결을 위해 so를 읽는 과정에서 특정 내부 함수를 찾을 수 없다는 에러이다.
 
한국의 메이저 안드로이드 제조사 것은 문제가 없어서 자체 테스트에서는 발견이 안 되었던 것이고, 화웨이, OPPO, HTC, ZET 등의 기기에서도 OS 5 버전에서만 문제가 발생했다.
 
[문제의 원인]
위의 에러 상황으로 검색을 해보니, 이런 버그 레포트가 올라왔고
 
최종 review confirm된 코드의 diff를 보면 다음과 같은 내용의 추가로 문제를 해결을 하였다.
 
## `__register_atfork` (Available in API level >= 23)
 
To allow `atfork` and `pthread_atfork` handlers to be unregistered on
`dlclose`, the implementation changed in API level 23. Unfortunately this
requires a new libc function `__register_atfork`. Code using these functions
that is built with a target API level >= 23 therefore will not load on earlier
versions of Android, with an error referencing `__register_atfork`.
 
*Resolution*: build your code with an NDK target API level that matches your
app's minimum API level, or avoid using `atfork`/`pthread_atfork`.
 
위의 패치의 결과로, 공식 매뉴얼에서 다음과 같이 추가되었으며 (글 중간에 있음)
 
결론적으로는, 앱의 minSDK API level과 NDK의 minSDK API level이 다른 것이 문제로 보였다.
API level 23 이후로 내부 standard so에 저러한 것들이 많이 추가가 된 모양이다.
 
[문제의 해결]
앱은 원래부터 minSDK를 19로 지정하고 있었지만, NDK는 이번에 발드를 위해 새로 프로젝트를 만드는 바람에 minSDK 는 현재 가장 높은 버전인 28로 맞춰져 있었다,'
 
그래서 NDK 빌드를 위한 프로젝트에서 app/build.gradle 를 android:defaultConfig:minSdkVersion : 28 -> 19 로 수정하여 빌드를 하였고 문제가 해결된 것을 nm으로 확인할 수 있었다.
 
<minSdkVersion 28일 때>
00000000001285f8 A __end__
                U __errno
                U __register_atfork  <- 여기
                U __stack_chk_fail
00000000001285f8 A _bss_end__
 
<minSdkVersion 19일 때>
00000000001295f8 A __end__
                U __errno
                U __sF
                U __stack_chk_fail
00000000001295f8 A _bss_end__
 
 
arm64와 x64 모두 동일하게 문제의 심볼인 '_register_atfork' 의 dynamic refrence가 사라진 것을 확인 하였다.

Posted by 안영기

2019/06/27 07:09 2019/06/27 07:09
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/53

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

컴파일 때 trigraph 에 대한 경고문

오랜만에 멀티플랫폼 빌드에서 특이한 경고를 보았다.
 
sprintf(s, "%s:?-??(??)", mi.pattern.c_str());

위의 코드가 문제의 코드인데, 다음과 같은 경고가 나왔다.
 
warning: trigraph converted to '[' character [-Wtrigraphs]
sprintf(s, "%s:?-??(??)", mi.pattern.c_str());
                 ^
warning: trigraph converted to ']' character [-Wtrigraphs]
sprintf(s, "%s:?-??(??)", mi.pattern.c_str());
                    ^
 
최근에는 거의 생각치도 않던 spec인 trigraph가 여기에 적용된 것인데,
예상을 못했던 것은 따옴표 안의 문장에서도 trigraph가 적용된다는 것이었다.
 
??( 는 [ 로, ??) 는 ] 로 변환이 되니, 문제의 
"%s:?-??(??)"
"%s:?-[]" 가 된다.

Posted by 안영기

2019/05/14 23:24 2019/05/14 23:24
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/52

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

비교적 최근부터, 개인적으로 만든 API server를 돌리면서 자체 app이나 자체 웹 페이지에서 그 기능을 이용하도록 하는 개인 프로젝트를 하고 있었다.

API server를 이용하는 app 중에는 Unity로 만든 것도 2개가 있었는데, 며칠 전에 그 중 하나의 GUI를 업데이트 하는 작업을 하였다. Unity의 테스트는 Windows에서 하기 때문에 개발 시의 테스트는 아무런 문제가 없이 잘 진행되었다. 그리고 최종 타겟 디바이스는 Android이기 때문에 마지막으로 디바이스에서 테스트만 하면 업데이트 테스트는 끝나는 상황이었다.

그런데, 그 중에 일부 기능이 타겟 동작을 하지 않았다.

<상황>
1. Windows에서는 잘 되던 app이 Android 디바이스에서는 제대로 동작하지 않았다.
2. 문제의 부분은 API server를 이용하는 부분이었다.
3. Server 쪽의 로그를 보면 app으로부터 아예 요청이 가지 않았다.
4. Unity 쪽에서 원인을 좁혀가니 www.error 가 "Unknown Error"가 발생하는 문제였다.
5. 네트워크와 관련된 부분은 이번에 code를 손댄 적이 없다.
6. 같은 API 서비스를 이용하는 또 다른 app은 문제없이 구동되고 있다.

흔한 오류 패턴은 아니었기 때문에 제일 먼저는 나의 실수인지를 체크했다.
잘 동작하고 있는 또 다른 app의 네트워크 부분과 diff 툴로 확인을 했지만 문제는 찾을 수가 없었다.

따로 방법이 없다보니 adb의 로그를 봐야 했다.

adb logcat -s Unity ActivityManager PackageManager dalvikvm DEBUG

그리고 유의미해 보이는 로그를 찾았다.

05-12 10:19:12.331 19641 19670 E Unity   : java.io.IOException: Cleartext HTTP traffic to api.******.net not permitted
05-12 10:19:12.344 19641 19657 I Unity   : ERROR: Unknown Error
 
저 'Cleartext HTTP traffic'라는 것이 생소해서 검색을 했더니, '안드로이드 9.0 파이(API level 28)'부터는 TLS 기반의 통신이 아니면 이 에러를 낸다고 한다.

https://android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html

즉, HTTP를 HTTPS로 바꾸어야 한다는 것인데, 그렇다고 당장에 server를 HTTPS로 바꾸는 것은 무리이다. 그리고 애시당초 내 쪽에서는 code를 손 댄 적이 없는데 발생하는 것이라서 더 원인을 알아 내야만 했다.

일단 1차적인 원인을 제거하기 위해서 Unity - Project Setting - Player 쪽으로 가서 Android의 빌드 버전을 바꾸기로 했는데 거기서 문제의 원인을 찾았다.
Android의 SDK가 다른 PC와의 작업을 위해서 Target API Level을 Automatic(highest installed) 로 했는데 그것이 문제였다.

최근에, Unity의 개발과는 별도로 또 다른 Android project를 위해 Android Studio를 업그레이드 하고 SDK도 가장 최신인 Pie를 추가로 설치했는데 이것이 문제의 발단이다.
원래는 제일 높은 버전이 27이었지만, 이 작업으로 인해 제일 높은 버전이 28로 바뀌게 되었고, Unity의 app을 Android용으로 재빌드를 하는 순간 API level 28이 적용되어 버린 것이었다.
 
사용자 삽입 이미지
결국 가장 빠른 문제 해결인 다음의 방법으로 해결하였다.
 
사용자 삽입 이미지

Posted by 안영기

2019/05/12 12:12 2019/05/12 12:12
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/51

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

기어2S의 Watchface 제작

(본문 내용은 추후에 넣을 예정)

Tizen 2.3.1 SDK의 wearable profile에는 Watchface App이라는 app 타입을 만들 수 있다.

<<Watchface app으로 등록>>
사용자 삽입 이미지


























<<일반 상태의 시간 출력>>
사용자 삽입 이미지


























<<Ambient 모드의 시간 출력>>
사용자 삽입 이미지


























만드는 방법은 다음의 소스를 참고하면 된다.
smwatch_151005a.7z

다운로드 (451K)



Posted by 안영기

2015/10/05 23:36 2015/10/05 23:36
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/48

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

C++11의 UTF-8, UTF-16, UTF-32 표기법

회사 일을 하면서, 사람들에게 설명을 해 줘도 또 다시 물어 보고 또 물어 보고 하는 것 중에 하나가 '문자 인코딩'이다. 지금은 예전과는 달리 거의 유니 코드로 통일이 되었기 때문에 그나마 많은 비교 설명을 할 필요까지는 없다. 하지만, 그대신에 유니 코드를 표기하는 방법 자체에 대해서는 질문이 많은 편이다.
 
UCS-2, UCS-4, UTF-8, UTF-16, UTF-32 등에 대한 자료가 인터넷에는 널려서 이해를 한다 하더라도, IDE에서 소스를 만드기 위해 사용되는 문자 인코딩, 그 안에서 글자를 출력하기 위한 문자 인코딩, 그리고 실제 그것이 C 코드에 버퍼로 적용되었을 때의 인코딩이 각각 다를 수 있다는 것을 이해 시키기는 아주 힘들다.
 
const char* str = "가나다"
-> VC++에서, 소스는 KSC-5601의 코드로 표현될 수 있고 실제 실행 버퍼에서도 KSC-5601일 수 있다.
-> VC++에서, 소스는 UCS-2의 코드로 표현될 수 있고 실제 실행 버퍼에서는 KSC-5601일 수 있다.
-> Ubuntu vi에서, 소스는 UTF-8의 코드로 표현될 수 있고 실제 실행 버퍼에서는 UTF-8일 수 있다.
 
const wchar_t* str = L"가나다"
-> VC++에서, 소스는 KSC-5601의 코드로 표현될 수 있고 실제 실행 버퍼에서도 UCS-2일 수 있다.
-> VC++에서, 소스는 UCS-2의 코드로 표현될 수 있고 실제 실행 버퍼에서는 UCS-2일 수 있다.
-> Ubuntu vi에서, 소스는 UTF-8의 코드로 표현될 수 있고 실제 실행 버퍼에서는 UTF-32일 수 있다.
 
위의 예를 실제 하나식 돌려 보고 디버거로 버퍼 상태를 본 것은 아니지만, 하여간 위와 같은 상황이 될 수 있다는 것에 대해서 설명하는 것은 아주 힘들다. 특히 OS를 건너 뛰며 코드를 복사를 해 와서 생긴 문제를 가지고 왔을 때는, 그 상황을 설명하려면 더 힘들다.
 
그런데 C++11 표준에서는 ""와 L""로 구분하던 버퍼 문자열의 문자 인코딩 표기법이 좀 더 세분화 되었다.
 
const char* str = u8"가나다";
const char16_t* str = u"가나다";
const char32_t* str = U"가나다";
 
와 같이, 각각 UTF-8, UTF-16, UTF-32 에 대한 표기법이 구체적으로 생겨났다. 이제는 컴파일러별로 그 크기나 문자 인코딩이 다를 수 있었던 wchar_t 에서 벗어나, 좀 더 명확하게 버퍼 문자열의 문자 인코딩을 지정할 수 있는 방법이 생긴 것이다.
 

Posted by 안영기

2015/03/22 21:34 2015/03/22 21:34
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/47

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

동급생2 맵출력 샘플 (Tizen 2.3 ver.)

이전에 Android NDK로 만든 것을 동일한 소스를 이용해 Tizen에서 구동하게 만들었다.
Tizen 2.3 에서 테스트 되었으며 일반적인 방법으로 import 를 하면 된다.

- 게임은 1995~6년도의 원작의 resource를 그대로 사용
  (ELF사에 저작권이 있는 부분입임)
- Cairo를 사용하여 scalability 구현
- 터치 입력을 추가, 스크린의 4 방향의 가장자리를 누르면 유이가 움직임

사용자 삽입 이미지

Tizen 2.3용 동급생 2 맵 출력 샘플 (88.7K)

Posted by 안영기

2014/11/23 01:59 2014/11/23 01:59
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/46

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

대변 파이터 (Tizen 이식作)

이번에는 DOS -> Windows -> WIZ -> CANNOO -> bada 를 거쳐 Tizen 플랫폼에도 대변 파이터를 이식해 보았다.


Tizen용 대변 파이터 (522K)

Posted by 안영기

2013/06/02 22:33 2013/06/02 22:33
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/45

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

동급생2 맵출력 샘플 (android ver.)

이전에 bada로 만든 것을 동일한 소스를 이용해 Android에서 구동하게 만들었다.

android-ndk-r8b 에서 테스트 되었으며 일반적인 NDK 샘플인 것처럼 import 를 하면 된다.


- 게임은 1995~6년도의 원작의 resource를 그대로 사용
  (ELF사에 저작권이 있는 부분입임)
- 당시 4-bit용 게임이었으므로 GetCanvasN()의 포맷인 ARGB8888로 리소스를 변경
- 터치 입력을 추가, 스크린의 4 방향의 가장자리를 누르면 유이가 움직임

Android용 동급생 2 맵 출력 샘플 (182K)

Posted by 안영기

2012/11/18 06:56 2012/11/18 06:56
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/44

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

API의 deprecate 방법

어제 API 관련된 스펙 논의를 하다가 다음과 같은 조건의 해결 방법에 대한 논의가 있었다.
(내용을 핵심만 간략하게 정리한 것)


class IObject
{
};

void RegisterObject(const IObject& obj)
{
}

위와 같은 형태의 API가 기존에 있었을 때, 이 API의 파라미터를 IObject* 로 받도록 바꾸고 싶지만 기존에 이미 만들어진 코드도 제대로 빌드되고 정상 동작하게 하려면 어떻게 해야 하는가?

물론 C 적으로는 다음과 같은 방법이 있을 수 있다.

void RegisterObject(const IObject& obj) __attribute__((deprecated)); // __declspec(deprecated)

void RegisterObject(const IObject& obj)
{
}

void RegisterObject(IObject* obj)
{
}


그리고  나의 경우는 다음과 같은 의견을 내었다.

template <typename T>
struct ParamAdaptor
{
inline ParamAdaptor(const T& obj)
: param(const_cast<T*>(&obj))
{
}

inline ParamAdaptor(T* obj)
: param(obj)
{
}

T* param;
};
class IObject
{
}

void RegisterObject(ParamAdaptor<IObject> objAdaptor)
{
}


이렇게 만들게 되면 다음의 코드가 모두 정상적으로 동작한다.

IObject obj1;
RegisterObject(obj1);
RegisterObject(&obj1);

IObject* obj2 = new IObject;
RegisterObject(*obj2);
RegisterObject(obj2);


물론 이것은 아이디어일뿐, API 사용자가 이 API를 사용할 때 헤더의 선언과의 괴리를 느끼게 되므로 이 방법이 채택되지는 않았다.

Posted by 안영기

2012/08/25 11:10 2012/08/25 11:10
Response
0 Trackbacks , 1 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/43

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

« Previous : 1 : 2 : 3 : 4 : 5 : Next »

블로그 이미지

게임 개발을 기반으로 한, 잡다한 개발 기록 저장소

- 안영기

Notices

Archives

Authors

  1. 안영기

Recent Comments

Recent Trackbacks

Calendar

«   2024/10   »
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Site Stats

Total hits:
247929
Today:
21
Yesterday:
27