메트로놈 Simple Metronome TIL 11
데이터가 같은데 결과가 다르면 원인은 데이터 밖에 있다
메트로놈을 켜면 첫 박만 유독 작게 들렸다. 둘째 박부터는 멀쩡했다.
소리 데이터를 보니 첫 박과 둘째 박은 크기가 똑같이 설정돼 있었다. 같은 값인데 위치만 다르고, 결과가 다르다. 그러면 아무리 데이터를 더 들여다봐도 답이 안 나온다. 원인은 우리가 만든 것 밖에 있다는 뜻이다.
이 판별이 조사 범위를 크게 줄여 줬다. 앱 안을 뒤지는 걸 멈추고 기기 쪽을 보기 시작했다.
무음 뒤 첫 소리는 기기가 눌러 버린다
원인은 콜드스타트 램프업이었다. 기기의 스피커·앰프는 완전한 무음 상태에서 소리가 시작될 때, 퍽 하는 잡음을 막으려고 출력을 0에서 서서히 올린다. 이 서서히 올라가는 구간이 램프업이다.
첫 클릭이 하필 그 구간에 떨어져서 눌린 것이다. 한 번 올라오고 나면 그 뒤는 정상이라 첫 박에서만 나타났다. 앱 코드로 켜고 끌 수 있는 설정이 아니라, 소리가 나가는 경로의 물리적 성질이다.
못 고치는 특성은 없애는 대신 미리 겪게 한다
우리가 못 고치는 것이 원인일 때 선택지는 둘이다. 포기하거나, 그 특성이 우리에게 손해가 안 되는 시점에 미리 일어나게 하는 것이다.
램프업은 소리가 있어야 시작된다. 그래서 진짜 첫 박이 나오기 전에, 사람 귀에 안 들릴 만큼 작은 소리를 0.15초쯤 먼저 흘려보냈다. 이렇게 본 내용 앞에 미리 깔아 두는 구간을 프리롤이라고 부른다. 프리롤이 램프업을 대신 겪어 주고 나면, 첫 박은 이미 올라온 경로로 나가서 안 눌린다.
일반화하면 이렇다. 첫 사용이 느리거나 부실한 시스템에서는, 사용자가 오기 전에 그 첫 사용을 대신 시켜 두면 된다.
방해하는 성질을 그대로 이용해 숨긴다
예열음은 안 들려야 한다. 그런데 완전히 안 들릴 만큼 작으면 램프업을 못 깨울 수도 있다.
여기서 앞의 성질을 거꾸로 썼다. 예열음을 시작은 크게, 끝으로 갈수록 0으로 줄어들게 배치했다. 큰 부분은 아직 경로가 안 올라온 구간에 놓이니 기기가 알아서 눌러 준다. 경로가 다 올라올 무렵엔 예열음이 이미 0이라 첫 박 직전은 조용하다. 방해하던 성질이 그대로 가림막이 됐다.
실제로 최대 볼륨에서 확인해 가며 크기를 낮췄고, 두 번 만에 안 들리는 선을 찾았다. 귀로 확인해야 하는 값은 계산으로 정하지 말고 실기에서 좁히는 게 빠르다.
기준점을 옮기면 그 기준에 매달린 것들을 같이 옮겨야 한다
예열을 넣자 소리는 고쳐졌는데 화면이 어긋났다. 두 번째 박이 울리는데 화면의 불은 첫 박에 들어와 있었다.
화면의 박 표시는 재생 헤드를 기준으로 켜진다. 재생 헤드란 지금까지 실제로 재생된 분량이 어디까지인지 가리키는 위치다. 그런데 이 위치는 재생을 시작한 지점을 0으로 놓고 센다. 앞에 예열을 끼워 넣었으니 실제 클릭은 전부 예열 길이만큼 뒤로 밀렸는데, 세는 쪽 원점은 그대로였다. 그래서 딱 한 박씩 어긋났다.
고치는 방법은 두 가지였다. 원점을 클릭 쪽으로 옮기거나, 어긋난 만큼 화면 점등을 늦추거나. 앞의 것은 재생을 잠깐 멈췄다 되돌리는 조작이 필요했고 그 과정에서 재생이 아예 안 되는 문제가 났다. 그래서 뒤의 것을 택했다 — 점등을 예열 길이만큼 늦춰서 소리와 같은 순간에 뜨게 했다.
교훈은 기술이 아니라 순서다. 무언가를 앞에 끼워 넣을 때는, 그 자리를 기준으로 세고 있던 것이 무엇인지 먼저 찾아야 한다. 소리만 확인하고 끝냈으면 화면이 어긋난 채로 나갈 뻔했다.
부품을 늘리면 기기별 편차에 노출되는 면도 넓어진다
예열을 넣는 길이 셋 있었다. 앞의 둘은 이 기기에서 실패했다.
- 예열 전용으로 소리 재생기를 하나 더 만들어 돌리는 방법. 첫 박은 잡혔는데 재생할 때마다 재생기를 새로 만드는 것이 간헐적으로 앱을 죽였다.
- 재생 위치를 0으로 되돌리는 방법. 예열이 다 나올 때까지 기다리는 부분이 끝나지 않아 재생이 시작조차 안 됐다.
결국 예열을 원래 쓰던 재생기 하나 안에 앞구간으로 합치고, 어긋남은 앞의 지연으로 처리했다. 재생기는 여전히 하나고 부수적인 조작도 없다.
부품을 하나 늘리면 기능이 하나 붙는 게 아니라, 기기마다 다르게 동작할 수 있는 면이 하나 더 생긴다. 안드로이드처럼 기기 편차가 큰 바닥에서는 이 비용이 특히 크다. 같은 결과를 낸다면 구조가 단순한 쪽을 고르는 게 안전하다.
사용자가 고를 수 있는 값의 양 끝은 실제로 눌러 봐야 한다
예열을 다 고치고 나서 느린 박자를 시험하다 앱이 꺼졌다. 분당 1박으로 두면 한 마디가 4분이다. 한 마디 분량을 통째로 담으려던 소리 저장 공간이 20MB를 넘었고, 그 크기를 요구하니 재생기가 아예 안 만들어졌다.
정상적인 박자에서는 절대 안 보이는 문제다. 사용자가 고를 수 있는 범위의 끝이라서 아무도 안 눌러 볼 뿐, 못 누르는 값은 아니다.
고친 방식도 판단이 하나 들어 있다. 우리 재생은 데이터를 계속 이어서 흘려보내는 스트리밍 방식이라, 저장 공간이 한 마디를 통째로 담을 이유가 없다. 필요한 만큼이 아니라 최대 몇 초까지라는 상한을 두는 것으로 바꿨다.
값의 범위를 열어 줬으면 그 양 끝은 반드시 눌러 본다. 그리고 크기가 입력에 따라 늘어나는 자리에는 상한을 둔다.
요약
- 데이터가 같은데 결과가 다르면 원인은 데이터 밖에 있다. 조사할 곳을 줄여 주는 판별이다.
- 우리가 못 고치는 특성이 원인이면, 없애는 대신 손해가 안 되는 시점에 미리 겪게 해서 우회한다.
- 방해하는 성질을 그대로 가림막으로 쓸 수 있는지 본다. 새 장치를 더하는 것보다 싸다.
- 앞에 무언가를 끼워 넣을 때는 그 자리를 기준으로 세던 것을 먼저 찾는다. 기준점을 옮기면 매달린 것도 같이 옮겨야 한다.
- 부품을 늘리면 기기마다 다르게 동작할 면이 같이 늘어난다. 결과가 같다면 단순한 구조를 고른다.
- 사용자가 고를 수 있는 값의 양 끝은 실제로 눌러 본다. 입력에 따라 크기가 늘어나는 자리에는 상한을 둔다.