G.711 코덱이란? mu-law, A-law와 8kHz 전화 음성

Rene, Kabir Goel 
G.711 코덱이란? mu-law, A-law와 8kHz 전화 음성

G.711은 음성 에이전트가 대부분의 전화 통화에서 듣는 코덱입니다. 음성을 초당 8,000번 샘플링하고, 각 샘플을 μ-law나 A-law라는 두 로그 곡선 중 하나로 8비트에 담아 64kbit/s로 보냅니다. ITU가 1972년에 G.711 권고로 처음 발표했고, 지금도 공중 전화망, 대부분의 SIP 트렁크, Twilio Media Streams에서 기본 형식입니다.

전화 음성을 다루는 무언가를 만든다면 언젠가는 G.711 바이트를 만지게 됩니다. 이 가이드는 코덱의 작동 방식, μ-law와 A-law를 구분하는 법, 몇 줄의 코드로 디코딩과 인코딩하는 법, 발신자의 목소리를 잡음으로 바꾸는 버그, 그리고 불필요한 변환 없이 8kHz 음성을 음성 모델과 주고받는 법을 다룹니다. 전화 회선을 음성 인식, LLM, 음성 합성에 연결하는 엔지니어를 위한 글입니다.

G.711 한눈에 보기

항목값
샘플링 레이트8,000Hz
샘플당 비트8 (선형이 아닌 로그)
비트레이트단방향 64kbit/s
오디오 대역약 300~3,400Hz
종류μ-law(PCMU)와 A-law(PCMA)
RTP 페이로드PCMU = 0, PCMA = 8, 둘 다 8,000Hz 클럭
20ms 패킷160샘플, 페이로드 160바이트
코덱 지연1샘플(0.125ms), 선행 탐색 없음
무음 바이트μ-law 0xFF, A-law 0xD5

20ms 패킷 크기는 RFC 3551에서 나오며, PCMU와 PCMA의 기본 패킷화 간격을 20ms로 정합니다. 대부분의 통신사와 PBX가 이 값을 쓰기 때문에 패킷 캡처에서 보게 되는 숫자는 패킷당 160바이트입니다.

μ-law와 A-law의 작동 방식

16비트 선형 샘플은 65,536단계를 표현할 수 있습니다. 음성에는 그렇게 많은 균등한 단계가 필요하지 않습니다. 필요한 것은 목소리 정보가 대부분 모여 있는 작은 음량에서의 세밀한 단계와, 작은 오차가 귀에 들리지 않는 큰 음량에서의 굵은 단계입니다.

G.711은 정확히 그렇게 합니다. 각 샘플을 로그 곡선에 대응시키는데, 이를 압신(압축한 뒤 디코딩할 때 신장)이라고 합니다. 8비트 코드는 세 부분으로 되어 있습니다. 부호 비트 1개, 8개 세그먼트 중 하나를 고르는 3비트, 세그먼트 안의 단계를 고르는 4비트입니다. 각 세그먼트는 바로 아래 세그먼트의 두 배 범위를 덮으므로, 신호가 커질수록 단계 폭이 두 배가 됩니다.

두 방식은 단계를 두는 위치가 다릅니다.

  • μ-law는 14비트 선형 샘플을 입력으로 받고 북미와 일본에서 씁니다. Twilio Media Streams가 주고받는 유일한 형식이기도 합니다.
  • A-law는 13비트 선형 샘플을 입력으로 받고 유럽, 인도, 그리고 대부분의 다른 나라에서 씁니다. 작은 신호에 약간 더 높은 해상도를 줍니다.

둘 다 13~14비트의 다이내믹 레인지를 8비트에 담기 때문에 G.711은 무전기가 아니라 평범한 전화처럼 들립니다. μ-law 국가에서 A-law 국가로 통화가 넘어가면 네트워크가 변환하고, 표준은 두 나라 사이 구간에서 A-law를 쓰도록 정합니다.

어느 방식인지 알아내는 법

보통 바이트만 봐서는 구분할 수 없습니다. 둘 다 헤더 없는 부호 없는 8비트 값의 나열입니다. 대신 주변 메타데이터를 보세요.

  • SDP. a=rtpmap:0 PCMU/8000이면 μ-law, a=rtpmap:8 PCMA/8000이면 A-law입니다.
  • Twilio Media Streams. start 메시지는 항상 "encoding": "audio/x-mulaw"와 "sampleRate": 8000입니다. Twilio의 WebSocket 메시지 문서는 이 값이 바뀌지 않는다고 명시합니다.
  • WAV 파일. fmt 청크의 포맷 태그는 μ-law가 7, A-law가 6입니다. ffprobe는 pcm_mulaw 또는 pcm_alaw로 표시합니다.
  • 무음. 조용한 회선에서 μ-law 바이트는 0xFF와 0x7E 근처에 머물고, A-law 바이트는 0xD5와 0x55를 오갑니다. 0xFF로 가득 찬 버퍼는 손상된 스트림이 아니라 μ-law 무음일 가능성이 큽니다.

잘못 고르면 귀로 들립니다. 440Hz 톤을 μ-law로 인코딩하고 A-law로 디코딩해 보니, 시험한 모든 음량에서 원본과의 상관이 절반 정도만 남았고, 음량은 입력 레벨에 따라 절반에서 두 배까지 흔들렸습니다. 방식을 잘못 고른 음성은 문제가 다른 곳에 있다고 착각할 만큼은 알아들을 수 있고, 음성 인식을 망칠 만큼은 거칩니다.

코드로 G.711 디코딩·인코딩하기

가장 빠른 방법은 FFmpeg입니다. 두 방식 모두 원시 스트림으로 읽고 쓸 수 있습니다.

# 통화의 원시 μ-law를 들을 수 있는 16비트 WAV로
ffmpeg -f mulaw -ar 8000 -ac 1 -i call.ulaw call.wav

# 아무 오디오 파일을 Twilio로 보낼 8kHz 원시 μ-law로
ffmpeg -i prompt.wav -ar 8000 -ac 1 -f mulaw prompt.ulaw

# A-law: 포맷 이름만 바꾸면 됩니다
ffmpeg -f alaw -ar 8000 -ac 1 -i call.alaw call.wav

Python에서는 예전에 표준 라이브러리의 audioop으로 했지만, audioop은 Python 3.13에서 제거되었습니다. audioop-lts 패키지로 되살릴 수 있습니다. 의존성을 늘리고 싶지 않다면 μ-law는 NumPy 몇 줄이면 됩니다.

import numpy as np

def linear_to_mulaw(pcm: np.ndarray) -> bytes:
    """16-bit signed PCM samples -> G.711 mu-law bytes."""
    x = pcm.astype(np.int32) >> 2                    # 14-bit magnitude range
    mask = np.where(x < 0, 0x7F, 0xFF)
    x = np.minimum(np.abs(x), 8158) + 33             # clip, then add the bias
    segment = np.floor(np.log2(x)).astype(np.int32) - 5
    code = (segment << 4) | ((x >> (segment + 1)) & 0x0F)
    return (code ^ mask).astype(np.uint8).tobytes()

def mulaw_to_linear(data: bytes) -> np.ndarray:
    """G.711 mu-law bytes -> 16-bit signed PCM samples."""
    u = ~np.frombuffer(data, dtype=np.uint8).astype(np.int32) & 0xFF
    segment, mantissa = (u >> 4) & 0x07, u & 0x0F
    magnitude = (((mantissa << 3) + 0x84) << segment) - 0x84
    return np.where(u & 0x80, -magnitude, magnitude).astype(np.int16)

두 함수를 16비트의 모든 입력값과 256개 μ-law 코드 전부에 대해 audioop과 대조했고, 완전히 일치했습니다. 인코더끼리는 세그먼트 경계에서 결과가 다를 수 있습니다(FFmpeg와 audioop은 65,536개 입력 중 512개에서 다릅니다). 차이는 곡선에서 한 단계이고 귀로는 구분되지 않으니 괜찮습니다.

발신자의 목소리를 잡음으로 바꾸는 버그

G.711 문제의 대부분은 바이트의 실제 내용과 코드가 생각하는 내용이 어긋나서 생깁니다.

  • 방식 혼동. μ-law를 A-law로 디코딩하거나 그 반대입니다. 소리가 크고 거칠며 반쯤 알아들을 수 있습니다. 통신사에 대한 짐작이 아니라 SDP나 스트림의 start 메시지를 확인하세요.
  • G.711을 8비트 선형 PCM으로 취급. 8비트 샘플을 선형이라고 가정하는 라이브러리는 목소리가 묻힌 잡음을 재생합니다. 리샘플링, 믹싱, 음성 활동 감지 전에 16비트 선형 PCM으로 디코딩하세요.
  • 스트림에 섞인 파일 헤더. Twilio는 보내는 media.payload에 오디오 파일 헤더 바이트를 넣지 말라고 합니다. WAV의 44바이트 헤더를 보내면 매 프롬프트 시작에 딸깍 소리나 잡음이 들어갑니다. 헤더를 빼거나 음성 모델에 원시 출력을 요청하세요.
  • 샘플링 레이트 표기 오류. 8kHz 음성을 16kHz로 표기하면 두 배 빠르고 한 옥타브 높게 재생됩니다. 반대면 느리게 돌린 테이프처럼 들립니다. 둘 다 오류 없이 디코딩되기 때문에 이런 버그는 코드 리뷰를 통과합니다.
  • 여러 번 리샘플링. 8kHz μ-law를 받아 한 서비스용으로 16kHz, 다른 서비스용으로 24kHz로 바꾼 뒤 다시 8kHz μ-law로 돌리는 파이프라인은 변환할 때마다 지연과 필터 잡음을 더합니다. 각 오디오 경로가 한 번만, 가능하면 한 번도 변환하지 않도록 형식을 고르세요.
  • “음질 개선”을 위한 업샘플링. 8kHz 음성을 16kHz로 바꿔도 4kHz 위는 돌아오지 않습니다. 늘어난 샘플은 보간된 값입니다. 16kHz만 받는 모델에는 도움이 되지만, 발신자의 “f”가 덜 “s”처럼 들리게 해 주지는 않습니다.

8kHz가 음성 에이전트에 의미하는 것

300~3,400Hz 대역은 대부분의 사람이 말을 알아듣는 데 필요한 부분이고, 그래서 전화가 통합니다. 하지만 “f”와 “s”, “m”과 “n”을 가르는 정보의 상당 부분이 이 대역 밖에 있습니다. 깨끗한 16kHz 브라우저 음성으로만 시험한 음성 인식 모델은 같은 말이라도 전화 회선에서는 눈에 띄게 나쁜 결과를 낼 수 있습니다. 통신사 쪽 이야기는 SIP 트렁킹 가이드에 있습니다.

도움이 되는 습관이 세 가지 있습니다.

  • 전화 음성으로 평가하세요. 모델을 비교하기 전에 실제 통신사를 통해 테스트 통화를 녹음하거나, 적어도 테스트 세트를 8kHz μ-law로 한 번 왕복 변환하세요. 결과를 잘못 읽지 않는 법은 WER 가이드에서 설명합니다.
  • 중요한 정보는 확인하세요. 이름, 이메일, 계좌 번호는 다시 읽어 주고, 긴 숫자열은 키패드 입력을 안내하세요. DTMF 키 입력은 코덱이 자음을 제대로 전달하는지와 상관이 없습니다.
  • 회선이 원하는 형식으로 음성을 보내세요. 44.1kHz로 생성한 합성 음성을 8kHz μ-law로 줄이면 청크마다 변환이 한 번씩 듭니다. 모델이 8kHz μ-law를 바로 만들 수 있다면 그렇게 요청하세요.

광대역 음성을 싣는 통화도 있습니다. G.722(“HD 보이스”)는 16kHz로 샘플링하고, WebRTC 통화는 보통 Opus를 씁니다. 하지만 통화가 공중 전화망을 거치는 순간부터는 G.711을 전제로 하세요.

Cartesia에서 G.711 쓰기

Cartesia의 음성 모델은 G.711을 기본으로 받고 출력하므로, 전화 파이프라인에 별도의 코덱 단계가 필요 없습니다.

음성 합성. Sonic은 요청하면 8kHz 원시 μ-law나 A-law를 돌려줍니다. 어떤 TTS 요청이든 출력 형식을 지정하세요.

{
	"container": "raw",
	"encoding": "pcm_mulaw",
	"sample_rate": 8000
}

A-law 회선에는 pcm_alaw를 쓰세요. 모든 인코딩과 레이트는 출력 형식 문서에 있습니다. TTS WebSocket의 원시 청크는 base64로 오는데, 이는 Twilio가 media.payload에 기대하는 형태와 같아서 Twilio 연동 가이드는 디코딩 없이 그대로 전달합니다.

음성 인식. Ink는 스트리밍 API로 μ-law와 A-law를 받습니다. 음성 에이전트용 턴 감지 엔드포인트도 마찬가지입니다. 연결할 때 형식을 선언하세요.

wss://api.cartesia.ai/stt/turns/websocket?model=ink-2&encoding=pcm_mulaw&sample_rate=8000&cartesia_version=2026-08-14

서버는 선언한 그대로 바이트를 디코딩하며, 선언이 틀려도 오류를 돌려주지 않을 수 있습니다. 오디오 입력 문서는 스트림을 저장해 ffplay로 확인하는 방법을 보여 주며, 잘못된 방식이나 레이트를 몇 초 만에 찾아냅니다.

Managed Agents. Managed Agents는 Cartesia 번호, Twilio, 또는 자체 SIP 트렁크로 전화를 받고 통화 음성을 대신 처리합니다. 자체 전화 시스템을 에이전트에 직접 연결한다면 에이전트 WebSocket이 mulaw_8000 입력을 바로 받습니다. 에이전트 통화는 분당 0.06달러이며, 플레이그라운드에서 만들어 내 전화로 걸어 볼 수 있습니다.

녹음 몇 개만 변환하면 된다면 FFmpeg로 충분하고, 음성 API는 필요 없습니다.

관련 가이드

자주 묻는 질문

G.711 코덱이란 무엇인가요?

G.711은 음성을 64kbit/s 펄스 부호 변조로 전송하는 ITU-T 표준입니다. 초당 8,000개 샘플, 샘플당 8비트이며, 각 샘플은 μ-law 또는 A-law 로그 곡선으로 압축되어 작은 소리일수록 더 세밀하게 남습니다. 1972년에 처음 발표되었고 지금도 대부분의 전화망과 SIP 트렁크에서 기본 코덱입니다.

μ-law와 A-law는 무엇이 다른가요?

둘 다 더 넓은 선형 샘플을 8비트에 담는 G.711의 압신 곡선입니다. μ-law(PCMU)는 북미와 일본에서, A-law(PCMA)는 유럽과 대부분의 다른 나라에서 씁니다. 공식이 달라서 한쪽으로 인코딩한 음성을 다른 쪽으로 디코딩하면 왜곡되어 들립니다. 두 지역을 오가는 통화는 네트워크가 변환합니다.

PCMU와 PCMA는 무엇인가요?

G.711의 RTP 이름입니다. PCMU는 μ-law로 정적 페이로드 타입 0을, PCMA는 A-law로 페이로드 타입 8을 씁니다. 둘 다 8,000Hz 클럭과 단일 채널로 동작합니다. SDP 오퍼, PBX 설정, SIP 트렁크 설정에서 볼 수 있습니다.

G.711은 PCM과 같은가요?

완전히 같지는 않습니다. 선형 PCM은 각 샘플을 보통 16비트의 단순한 숫자로 저장합니다. G.711은 로그 압축을 적용한 PCM으로, 샘플당 8비트라 같은 샘플링 레이트의 16비트 PCM보다 크기가 절반입니다. 대부분의 도구에서 G.711 음성을 다루려면 먼저 16비트 선형 PCM으로 디코딩합니다.

G.711이 음성 인식 정확도를 떨어뜨리나요?

인식 모델이 들을 수 있는 범위를 제한합니다. G.711은 대략 300~3,400Hz를 전달하므로 그보다 높은 대역에 있는 소리, 예를 들어 "f"와 "s"의 차이는 구분하기 어려워집니다. 업샘플링해도 잃어버린 대역은 돌아오지 않습니다. 8kHz μ-law나 A-law를 그대로 받는 음성 인식 모델을 쓰고, 실제 전화 경로의 녹음으로 테스트하세요.