오래간만에 괜찮은 DIY 하나 소개드립니다.

 

취미로 게임 영상과 함께 본인의 목소리를 녹화(녹음)를 많이들 하시는 것으로 알고 있습니다.

그런데 모바일 게임의 경우 대부분은 순정 이어폰으로 녹화를 동시에 하게 되는데 이 경우 게임 소리는 녹화가 되지 않는 문제가 있습니다. (예외는 있지만 보통의 경우 불가)

 

이 문제를 해결하고자 비싼 가격의 특수 이어폰을 구입하시거나 별도 편집(목소리 따로, 게임 소리 따로 녹음 녹화 후 합치기)등의 방법을 사용해야 합니다.

 

그래서 이번 DIY는 비싼(?) 가격의 특수 이어폰을 구입하지 않고, 직접 만드는 방법을 소개드립니다.

저의 경우 취미가 이 분야라 집에 있는 부품만으로 모두 해결해서 실질 투자는 저의 인건비만 들었습니다.

 

준비물

  • 납땜장비 (납땜인두, 납, 플럭스 등등)

  • 테스터기 (연결 확인용 저항 측정)

  • 저항 - 1K 옴 2개 

  • 저항 - 100 옴 1개 

  • 0.1uF(마이크로패럿) 캐페시터 1개 (종류는 작은 전해, 탄탈, 세라믹(굴러다니는 것) 중에 선택합니다)

  • 마무리용 재료 (본드 or 실리콘, 열수축 튜브, 케이블 타이 소형)

회로도

회로도는 아래의 사진처럼 매우 간단(?) 합니다.

전자 공학을 전공하지 않아 유창하게 설명은 곤란하나 간단히 설명을 드리면 아래와 같습니다.

  • 이어폰으로 좌/우 음성 출력을 각각 1K 저항을 거쳐 마이크 입력 임피던스에 적절하게 맞추어(낮추어) 줍니다.

  • 합쳐진 음성 출력을 0.1uF 캐페시터를 이용하여 직류 성분이 빠진 음성 성분만 통과시켜 마이크 입력 처리

  • 요약: 음성 출력을 마이크 입력과 합쳐 주어서 내부 소리를 마이크로 입력(녹음) 처리하는 것입니다.

  • Option이라 표기한 부분은 없어도 될 것 같아 처음 그린 후 실제 적용하지 않았습니다.

개조 회로도

DIY 핵심 사항 

납땜 좀 하셨던 분이나 취미로 이 분야를 하신 분은 위 회로도만 보고 알아서 잘하시리라 믿습니다.

하지만 보통의 분이라면 아래 순서를 참고하셔서 진행하세요.

아래 순서로 DIY를 진행합니다.

  1. 이어짹 한 뼘 떨어진 위치에서 전선의 외부 피복을 벗겨 냅니다. (내부 선을 손상하면 안 됩니다)

  2. 내부 전선을 살살 서로 떼어 냅니다 - 보조도구를 적절히 이용하세요 (전선 묶는 타이 등등으로)

  3. 개별 선을 사진을 참고하여 칼로 살짝 내부 선이 보이도록 피복을 벗겨 냅니다.

  4. 인두로 벗겨진 선을 납이 약간 묻은 이두로 가열하여 전선을 감싸는(에나멜) 부분을 벗깁니다(도통이 되도록)

  5. 테스터기 짹의 L/R/GND/MIC 부분과 벗겨낸 부분을 저항을 측정하여 1~2옴 저도가 나오는지 확인

  6. 3~5 과정을 반복하여 L/R/GND/MIC 신호를 모두 찾아냅니다. (본 DIY에서 GND는 연결하지 않습니다)

  7. 회로와 동일한 구성이 되도록 저항(1K 옴)을 L / R 신호선과 각각 납땜합니다.

    (선이 가늘고 에나멜이 묻어 있기에 약간의 플럭스와 함께 납땜을 합니다.)

  8. 7에서 L / R로 납땜한 저항 반대 부분을 합쳐서 납땜 (신호 합치기)

  9. MIC 신호선과 저항(100옴)을 납땜 (7번 과정 참조)

  10. 캐페시터를 MIC와 연결한 저항 끝단(9)과 L / R 신호와 연결한 저항 끝단(8) 단자와 각각 연결 합니다.

아래 사진은 저항 3개와 캐패시터 한 개를 모두 회로에 맞게 납땜한 결과입니다.

다른 각도로 본 납땜 결과

또 다른 각도로본 납땜 결과

실전 Test

  1. 모든 부품을 잘 납땜했는지 테스터기를 통해서 연결을 확인 합니다.

  2. 이어짹을 휴대폰과 연결 하여 실전 test를 합니다. 녹음기 앱을 실행 후 배경에 음악을 동작 시킨 후 녹음을 시작 하시고 음량을 중간 이하로 두시고 녹음 해 봅니다.

  3. 배경 음악과 함께 본인 음성이 녹음이 되었다면 성공 입니다

 

실리콘, 혹은 E6000 과 같은 물렁하게 응고되는 접착제로 마무리

- 선이 가는 관계로 외부 충격에 약하기(납땜 분리 등)에 꼭 아래와 같이 혹은 동등 수준으로 고정 합니다.

압축 튜브로 외관 정리 + 케이블 타이로 고정

 

이상 값 싸게! 스마트폰 내부 소리와 함께 본인의 음성을 녹음 할 수 있는 순정 이어폰 개조 DIY를 마무리 했습니다.

 

많은 모바일 유튜버에게 도움이 되었으면 합니다. 

갤럭시 탭 S4용 충전 거치대(포고핀 충전 거치대 (Charging dock pogo) EE-D3100)를 사용하기 위해서는 반드시!!! 키보드 커버는 벗기고 거치가 가능합니다.

아시겠지만 커버 벗기는 게 원터치로 되는게 아니고 약간의 기술이 필요합니다. 즉 벗기는게 어렵다는 것!

 

그래서 커버를 벗기지 않고 사용하기 위해, 키보드 커버를 개조하였습니다.

쉽게 충전독의 뒤쪽 받침과 걸리는 부분을 과감히! 칼로 잘라 내어 사용하는 방법을 간단히 정리해 보았습니다.

핵심: 충전독과 걸리는 위치의 커버 하부를 과감히 자르면 OK (쪼금만 자르면 됩니다)

 

말보다는 사진 설명이 쉬습니다.

 

거치대와 끼워지는 부분을 확인하시고 잘라야 할 너비를 표기 후 아래와 같이 자르기 시작합니다.

부드러운 플라스틱 재질이라 큰 칼로 여러 번(좀 강하고 많이) 사정없이 그어 주세요

핵심 준비물은 칼!

 

한참을 반복 하셔야 합니다. 안전하게 힘조절 하시고

 

잘리기 시작하면, 양쪽 끝단도 잘라 냅니다.

 

플라스틱 양쪽에 붙여져 있는 마감재도 예쁘게 칼로 마무리 합니다.

 

완성 - 이정도만 자르면 OK 입니다.

 

Tab S4와 재결합 전 모습 입니다. 결함 후에는 티가 나지 않습니다.

 

키보드 커버를 장착 한 채로 충전거치대와 도킹!
옆에서 보면 이런 모습입니다. 흉할수도 있겠지만 이렇게라도 바로 장착이 되는게 어딥니까?

갤럭시 탭 S4(Galaxy Tab S4)와 갤럭시 탭 A4 모델용 충전 거치대입니다.

공식 가격이 쓸데없이 비싼 가격인데 고속 충전기가와 케이블이 포함되어 그런 것 같습니다.

 

사진 몇 장 나갑니다.

문제 아닌 문제가 키보드 커버(다른 커버는 더욱더)를 장착한 상태로는 충전독에 거치할 수 없습니다.

이 부분을 DIY로 해결한 내용도 이어서 올립니다.

 

케이스

 

개봉 후 본체가 먼저 보입니다.

 

본체 아래에 위치한 고속 충전기와 USB C 충전 케이블

 

맨 아래에는 간단 사용 설명서가 있습니다.

 

거치대 - 약간의 무게감이 있는 묵직함을 느낄 수 있습니다.

 

본체 하단에 위치한 제품 설명 딱지

 

갤럭시 탭 S4 거치 후 정면

 

갭 S4 - 비스듬히 위로 본 모습
갭 S4 - 비스듬히 위로 본 모습

 

거치대만 정면 위

 

나름 깔끔한 날씨 프로그램인 eWeather HD를 Android P OS까지 문제없이 사용하고 있었습니다.

그런데 Galaxy S9을 최신 Q OS 업그레이드 직후 eWeather HD 실행, 화면이 보이는 동시에 crash가 나고 죽어 버립니다.

 

죽는 위치는 dump 하면 쉽게 확인됩니다.

JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c

from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)

 

crash log 중요 내용은 아래와 같습니다.

02-17 02:25:19.945 10426 13450 16754 F nt.WeatherCloc: java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c
02-17 02:25:19.945 10426 13450 16754 F nt.WeatherCloc: java_vm_ext.cc:570]     from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)
...
...
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] Runtime aborting...
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] Dumping all threads without mutator lock held
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] All threads:
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] DALVIK THREADS (17):
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] "AsyncTask #1" prio=4 tid=7 Runnable
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | group="" sCount=0 dsCount=0 flags=0 obj=0x12e5fe20 self=0xd6567800
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | sysTid=16754 nice=10 cgrp=default sched=0/0 handle=0xc3208230
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | state=R schedstat=( 33144883 457271 28 ) utm=1 stm=1 core=5 HZ=100
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | stack=0xc3105000-0xc3107000 stackSize=1040KB
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | held mutexes= "abort lock" "mutator lock"(shared held)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #00 pc 002fe4ef  /apex/com.android.runtime/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+78)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #01 pc 003a768b  /apex/com.android.runtime/lib/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+358)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #02 pc 003a3e63  /apex/com.android.runtime/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+34)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #03 pc 003bc485  /apex/com.android.runtime/lib/libart.so (art::DumpCheckpoint::Run(art::Thread*)+576)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #04 pc 003b70c7  /apex/com.android.runtime/lib/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+354)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #05 pc 003b67ad  /apex/com.android.runtime/lib/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool)+1416)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #06 pc 00376953  /apex/com.android.runtime/lib/libart.so (art::Runtime::Abort(char const*)+1058)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #07 pc 0000855f  /system/lib/libbase.so (android::base::LogMessage::~LogMessage()+406)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #08 pc 0028806f  /apex/com.android.runtime/lib/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1194)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #09 pc 00288211  /apex/com.android.runtime/lib/libart.so (art::JavaVMExt::JniAbortF(char const*, char const*, ...)+64)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #10 pc 003ac06f  /apex/com.android.runtime/lib/libart.so (art::Thread::DecodeJObject(_jobject*) const+538)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #11 pc 00417905  /apex/com.android.runtime/lib/libart.so (_ZN3artL37JniMethodEndWithReferenceHandleResultEP8_jobjectjPNS_6ThreadE.llvm.5386863320803738609+36)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.ArmManager.ARMPluginMakeChallenge(Native method)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.ArmManager.a(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.f.a(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.f.doInBackground(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at android.os.AsyncTask$3.call(AsyncTask.java:378)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.FutureTask.run(FutureTask.java:266)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.lang.Thread.run(Thread.java:919)
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] 
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:649] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:649]     from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)
...
...
--------- beginning of crash
02-17 02:25:20.230 10426 13450 16754 F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 16754 (AsyncTask #1), pid 13450 (nt.WeatherClock)
02-17 02:25:20.251  1000  4997  5086 E libprocessgroup: Failed to kill process cgroup uid 10158 pid 13776 in 206ms, 1 processes remain
02-17 02:25:20.279 10426 16786 16786 E crash_dump32: unknown process state: t
02-17 02:25:20.290 10426 16786 16786 I crash_dump32: obtaining output fd from tombstoned, type: kDebuggerdTombstone
02-17 02:25:20.295  1058  4758  4758 I /system/bin/tombstoned: received crash request for pid 16754
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: pcf : 0x1012, 0 ,2 ,0 ,0 ,0 ,2, 5.0.0.0
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: thread id : 2, preenroll_flag : 0, nd cnt : 1, cso : 0, et : 0
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: FPBAuthService, 10979
02-17 02:25:20.296 10426 16786 16786 I crash_dump32: performing dump of process 13450 (target tid = 16754)
02-17 02:25:20.307 10426 16786 16786 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Build fingerprint: 'samsung/starlteks/starlteks:10/QP1A.190711.020/G960NKSU2DTAB:user/release-keys'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Revision: '26'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : ABI: 'arm'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Timestamp: 2020-02-17 02:25:20+0900
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : pid: 13450, tid: 16754, name: AsyncTask #1  >>> com.Elecont.WeatherClock <<<
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : uid: 10426
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Abort message: 'JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c

대충 보면 skt에서 제공하는 so library의 특정 native 함수에서 죽는 문제입니다.

업데이트가 매우 오래전에 멈추어진 App.이라 그럴 수도 있겠죠.

 

핵심 호출 부분은 아래입니다.

at com.skt.arm.f.a(unavailable:-1)

at com.skt.arm.f.doInBackground(unavailable:-1)

 

즉 doInBackground 안에서 호출하는 com.skt.arm.f.a 호출 흐름에서 죽습니다.

이걸 해결할 방법은 없고 대신 호출을 막습니다.

그리고 test하니 - SKT 서비스가 없다면서 App. 을 강제 종료 해 버립니다 우쓰!

 

그래서 doInBackground 종료 후 호출되는 onPostExecute 의 주요 호출을 막으니, 정상 동작이 됩니다.

아마다 예전 SKT 관련 서비스 유무 확인 혹은 여러 내용(라이센?!)을 확인하는 루틴이 Q OS에서 동작이 되지 않은 듯 합니다.

 

어찌 되었건 문제는 해결되었습니다.

비록 오래된 App.이지만 더 이상 update 되지 않는 버전이지만 문제만 해결해서 계속 사용하게 되었습니다.

** 해당 App. 은 Play store에 최신 버전의 App. 이 제공되지만 예전 T Store 와는 다른 signature 사용으로 인해 업그레이드를 할 수 없습니다.

 

주요 수정 point는 아래와 같이 요약할 수 있습니다. 단순히 smali 수준에서 문제 함수를 막아서 해결했습니다.

맨 앞에 # 한 부분입니다.

# virtual methods
.method protected final varargs synthetic doInBackground([Ljava/lang/Object;)Ljava/lang/Object;
    .locals 1

#    invoke-direct {p0}, Lcom/skt/arm/f;->a()Ljava/lang/String;

#    move-result-object v0
	const-string v0, " Not ACTIVITY"

    return-object v0
.end method

.method protected final synthetic onPostExecute(Ljava/lang/Object;)V
    .locals 1

    check-cast p1, Ljava/lang/String;

    invoke-super {p0, p1}, Landroid/os/AsyncTask;->onPostExecute(Ljava/lang/Object;)V

#    iget-object v0, p0, Lcom/skt/arm/f;->a:Lcom/skt/arm/e;

#    invoke-static {v0}, Lcom/skt/arm/e;->a(Lcom/skt/arm/e;)Lcom/skt/arm/ArmManager;

#    move-result-object v0

#    invoke-static {v0}, Lcom/skt/arm/ArmManager;->b(Lcom/skt/arm/ArmManager;)V

    return-void
.end method

위 내용 참고요 스크린 샷 (APK Studio)

APK Studio to fix the crash

* 수정 후 정상 동작 스크린 샷~

 

 

** 리버스 엔지니어링은 더이상 업그레이드가 제공되지 않는 S/W 에 대한 호환성 확보를 위한 목적에는 적법합니다.

컴퓨터프로그램 보호법 - 역분석 관련 법

제12조의2 (프로그램코드역분석)
①정당한 권원에 의하여 프로그램을 사용하는 자 또는 그의 허락을 받은 자가 호환에 필요한 정보를 쉽게 얻을 수 없고 그 획득이 불가피한 경우 당해 프로그램의 호환에 필요한 부분에 한하여 프로그램저작권자의 허락을 받지 아니하고 프로그램코드역분석을 할 수 있다.
②제1항의 규정에 의한 프로그램코드역분석을 통하여 얻은 정보는 다음 각호의 1에 해당하는 경우에는 이를 사용할 수 없다.
   1. 호환 목적외의 다른 목적을 위하여 이용하거나 제3자에게 제공하는 경우
   2. 프로그램코드역분석의 대상이 되는 프로그램과 표현이 실질적으로 유사한 프로그램을 개발·제작·판매하거나 기타의 프로그램저작권을 침해하는 행위에 이용하는 경우
      [본조신설 2001·1·16][[시행일 2001·7·17]]

 

KENWOOD MJ919

무려 2000년도에 구입한 1DIN 카오디오인 KENWOOD MJ919 내부 동작 동영상을 공유합니다.

첫차(1997년 구입한 94년식 엑센트 1.5CS 중고)의 순정 오디오를 대신하여 2000년부터 폐차 시점까지 잘 사용하던 MiniDisc 리시버입니다.

당시에 한참 MD가 인기를 끌던 때이고 힘들게 일본 여행 가는 후배에 부탁하여 거금(?!)을 들여 구입하였던 물건입니다.

당시에는 인터넷 쇼핑이 지금처럼 활발하기 전이라 직접 구입 방법이 유일했습니다.

지금은 KENWOOD가 합병도 되고, 사제 카오디오 수요가 거의 죽다시피 한 상황이라 지금과는 매우 다른 환경이었습니다.

 

QM5 구입하고 한동안 이삿짐으로만 생명을 연장하던 녀석을 사운드바 구입 후 생명 연장을 한 기념으로 내부 체인져 동작 연상을 촬영하여 공유합니다. (삽입, 인출이 잘 되지 않는 문제 수정 겸)

1DIN 규격의 작은 공간에 무려 4장의 MD를 넣을 수 있는 신기한 물건이기도 했고, 지금 보아도 앞부분의 그래픽이 매우 화려(비록 컬러는 아니지만)하고 동작 하나하나가 인상 적인 물건입니다.

 

 

KENWOOD 3+1 MiniDisc Changer Receiver (2000)

+ Recent posts