SoundFlow 음성 연속성 패치 실기기 검증
결론: A1 실기기에서 원본
sndflow.ko의 정확한 64샘플, 4 ms zero-fill을 2건 재현했다. 두 사건은 8~16 ms 뒤 원본 64샘플 skip도 동반했다. 공통cur_jiffies패치 후에는 더 긴 63.042 stream-min 동안 64-zero와 phase jump가 모두 0건이었다. Queue overflow도 실기기에서 확인됐으며,pop_front()패치는 한 사건의 손실 상한을 약 320 ms에서 약 64 ms로 줄였다. 다만 overflow의 근본 처리 지연과 KWS/STT 회귀는 별도 과제다.
1. 검증 환경
| 항목 | 값 |
|---|---|
| 기기 | QTI lahaina A1, Android 13 userdebug |
| Build | A1_BUILD3_732 |
| Kernel | 5.4.274-qgki-consolidate, aarch64, PREEMPT |
| Kernel tick | CONFIG_HZ=250, 1 tick = 4 ms |
| Kardome | 1.1.17 |
| 시험 신호 | 16 kHz, PCM16, mono, 모든 샘플 non-zero |
| ALSA 조건 | playback period 512 / capture period 640 |
| 추가 부하 | 원본·패치 각각 ADB read 약 192 MB |
설치 전에 원본 서비스, 모듈, Kardome library를 pull하고 SHA-256을 기록했다. 모듈 vermagic와 기기 커널도 일치했다.
| 파일 | 원본 SHA-256 앞 8자리 | 패치 SHA-256 앞 8자리 |
|---|---|---|
| SoundFlow service | 0a50e417 |
409e10ff |
sndflow.ko |
a2216c74 |
3e5b9b9e |
libKardome.so |
8f19cb7d |
동일 |
서비스와 .ko는 파일 교체만 하지 않고 매 조건마다 재부팅하여 실제
로드된 상태를 확인했다.
2. 재현 방법
SoundFlow ALSA loopback의 독립 substream에 규칙적인 non-zero 신호를 재생하고 반대편을 동시에 녹음했다.
sample = 2000 + ((sample_index × 37) mod 12001)
정상 신호에는 0이 없고 다음 샘플도 정확히 계산할 수 있다. 따라서
다음을 동시에 판정할 수 있다.
- 정확히 64개인 hard-zero run
- 중복 또는 source skip을 뜻하는 phase jump
- 단순 정적이나 zero-crossing과의 구분
분석 결과는 재현 가능한 Python 분석기와 metrics.csv로 남겼다.
3. 원본에서 재현된 4 ms 손상
원본 장시간 회차의 유효 direct loopback 노출은
59.987 stream-min이었다.
| 증거 | zero 구간 | zero 길이 | 후속 source skip |
|---|---|---|---|
| stream 7 | 49.952~49.956초 | 64샘플, 4 ms | zero 종료 8 ms 뒤 64샘플 |
| stream 4 | 349.984~349.988초 | 64샘플, 4 ms | zero 종료 16 ms 뒤 64샘플 |
stream 4의 zero 경계는 다음과 같다.
직전: ... 12996, 13033, 13070
문제: 0 × 64 samples
직후: 13107, 13144, 13181 ...
zero 직후에는 13070의 정상 다음 값인 13107이 이어진다. 그러나
뒤쪽 buffer 경계에서는 정상 1-step 대신 65-step 전진하여 정확히
원본 64샘플을 건너뛴다.
4 ms zero-fill
-> 8~16 ms 동안 일시적으로 정상 연속
-> buffer 경계에서 4 ms source skip
이 결과는 CONFIG_HZ=250의 한 tick인 4 ms와 WAV 손상 크기가
일치한다는 기존 원인 후보를 실기기에서 직접 뒷받침한다.
4. 패치 후 A/B 결과
사용자의 종료 요청까지 패치 후 7개 direct pair를 각각 541.44초
녹음했다. 마지막 100 ms는 14개 tinycap/tinyplay를 동시에
SIGINT로 닫은 종료 경계로 별도 분류했다.
| 지표 | 원본 | 패치 |
|---|---|---|
| 유효 direct loopback | 59.987 stream-min | 63.042 stream-min |
| 64샘플 zero | 2건 | 0건 |
| 64샘플 source skip | 2건, 합계 128샘플 | 0건 |
| 정상 운용 continuity failure | 2 stream | 0 stream |
| 강제 종료 경계 전이 | 0건 | 4건 |
패치 파일의 종료 전이는 마지막 40~76 ms에만 몰려 있었다. 정상 운용 구간에는 64-zero, phase jump, source skip이 없었다.
이 검증은 공통 cur_jiffies 패치의 실기기 근거다. 다만 유한 시간
시험이므로 모든 운용 조건에서 발생 확률이 수학적으로 0이라고
증명한 것은 아니다.
5. Queue overflow 실기기 결과
패치 서비스 부팅 후 자연 운용 중 다음 로그가 발생했다.
07-30 12:00:45.321 W SoundFlowInput:
threadLoop : input overflow, dropped oldest buffer (dropped=1, queued=4)
입력과 처리 thread 시작 약 9초 뒤의 실제 overflow다.
| 정책 | 동작 | 사건당 최대 손실 |
|---|---|---|
| 원본 | 5개 queue 전체 clear() |
약 320 ms |
| 패치 | oldest 1개 pop_front() |
약 64 ms |
한 buffer는 1,024 frames이며 16 kHz에서 64 ms다. 로그의
queued=4는 oldest 하나를 버리고 최신 네 개를 유지한 구현과
일치한다.
중요한 해석:
- 실기기에서 queue가 실제로 밀리는 조건은 존재한다.
- 패치는 대량 삭제를 줄이지만 drop을 0으로 만들지 않는다.
- AFE, KWS,
pcm_write(), dump I/O 중 어느 단계가 지연됐는지는 buffer별 처리시간 계측이 필요하다.
6. KWS/STT 영향과 한계
- 4 ms zero와 source skip은 자음 onset이나 음소 경계를 국소적으로 훼손할 수 있다.
- 320 ms 삭제는 음절 또는 짧은 단어 일부를 통째로 생략할 수 있어 KWS miss와 STT deletion 위험이 더 크다.
pop_front()는 사건당 손실을 약 64 ms로 줄이지만 반복 overflow에서는 인식 회귀가 계속 가능하다.- 이번 시험은 파형 연속성 검증이다. KWS FRR/FAR와 STT CER/WER를 측정하지 않았으므로 인식 성능 개선 판정으로 확대하지 않는다.
7. 최종 기기 상태
기기는 패치 상태로 유지했다.
init.svc.soundflowservice = running
sys.audio.sndflow.krdm.version = 1.1.17
service SHA-256 = 409e10ffa77925c504f4aea21eaf84f95496aabd929d20604a1b345be3c23f56
sndflow.ko SHA-256 = 3e5b9b9e24ac25011841331e944203de0e8b181a2b7ff5063d2665717abbeeb1
libKardome.so SHA-256 = 8f19cb7d96e858a5a23d01353a07dea54ce6d26323f619af67c0934771e32afe
최종 패치 시험 logcat:
- queue overflow 추가 로그 0건
- SoundFlow I/O 오류 0건
- fatal/linker 오류 0건
8. 다음 Gate
- AFE, KWS,
pcm_write(), dump I/O의 buffer별 처리시간과 sequence를 기록한다. - 정상 운용 30분 이상에서 overflow 누계와 서비스 RSS/PSS를 확인한다.
- 동일 발화 paired capture로 KWS FRR/FAR와 STT CER/WER를 비교한다.
기존 원인 분석과 코드 경계는 SoundFlow 음성 손상·Zigging 원인 분석을 참조한다.