Prosody

추가 기능 · SSML

SSML

DIVE 요청의 text에 SSML 1.1 문서를 넣어 쉼, 읽는 방식, 속도·높이·음량, 목소리, 효과음, 위치 마크를 조절합니다.

1

SSML로 합성하기

일반 텍스트 요청과 같은 엔드포인트·필드를 쓰고 text만 SSML 문서로 보냅니다.

지원 엔드포인트

POST
https://api-console.humelo.net/api/v1/dive
POST
https://api-console.humelo.net/api/v1/dive/stream
GET
wss://api-console.humelo.net/api/v1/dive/ws
  • 요청의 text가 <speak로 시작하는 문자열이면 SSML 문서로 해석합니다. 별도 필드나 헤더는 없습니다. 앞의 공백·XML 선언·주석은 건너뜁니다.
  • DIVE Standard, Streaming, WebSocket에서 쓸 수 있습니다.
  • SSML 1.1의 일부를 지원합니다. 엔진이 표현할 수 없는 효과는 오류 없이 내용만 읽고, 잘못된 문서·값은 400으로 거부합니다.
  • text는 문자열 하나여야 합니다. 배열 text에 SSML을 넣으면 400입니다.
  • 요청의 speed, pitch, volume, sentenceSilenceSec, dictionaryId는 문서 전체의 기본값으로 쓰이고 SSML 태그가 그 위에 적용됩니다.

인증 및 헤더

API Key는 서버 환경변수에 보관하고 요청 헤더로만 전달하세요.

http
X-API-Key: {YOUR_API_KEY}
Content-Type: application/json

요청 예시

bash
curl -X POST https://api-console.humelo.net/api/v1/dive \
  -H "X-API-Key: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "voiceName": "나나",
    "emotion": "neutral",
    "outputFormat": "mp3_48000_128",
    "text": "<speak>안녕하세요.<break time=\"500ms\"/>오늘 날짜는 <say-as interpret-as=\"date\" format=\"ymd\">2026-10-23</say-as>입니다.</speak>"
  }'

글자 수와 과금

  • 글자 수와 과금은 태그를 뺀 읽을 텍스트로 셉니다. sub는 alias, phoneme은 ph, say-as는 숫자 등을 읽기 전 원문 글자를 셉니다.
  • audio, break, prosody duration으로 늘어난 길이는 과금하지 않습니다.
2

지원 요소

아래 요소와 속성을 해석합니다. 표에 없는 SSML 요소·속성은 400이고, 다른 네임스페이스(접두사)의 요소는 태그를 무시하고 내용을 읽습니다.

문서 구조

문서 구조
요소속성동작
<speak>version, xml:lang, xml:base, startmark, endmark문서 루트입니다. version은 1.0·1.1이며 생략할 수 있습니다. xml:lang(ko, en)은 요청 lang보다 우선합니다. startmark·endmark를 주면 그 마크 사이만 합성합니다. startmark가 endmark보다 뒤에 있으면 읽을 내용이 없어 400입니다.
<p>, <s>xml:lang문단·문장 경계입니다. 문장 사이에는 sentenceSilenceSec(기본 0.4초), 문단 사이에는 그 2배(최대 2초)를 쉽니다.
<meta>, <metadata>, <desc>—읽지 않습니다.

쉼

쉼
요소속성동작
<break>time, strengthtime(예: 500ms, 1.5s, 최대 10초) 또는 strength만큼 쉽니다. 속성이 없으면 medium입니다. 같은 자리의 문장·문단 쉼을 대신합니다.

읽는 방식

읽는 방식
요소속성동작
<say-as>interpret-as, format, detail숫자·글자·날짜·시각·전화번호를 읽는 방식을 정합니다. 아래 say-as 표의 6개 값을 지원합니다.
<sub>alias내용 대신 alias를 읽습니다. 약어·기호에 씁니다.
<phoneme>alphabet, phalphabet="x-hangul"(기본)이면 ph에 적은 한글 표기대로 읽습니다. ipa 등 다른 표기법은 효과 없이 내용을 읽습니다.
<lexicon>, <lookup>uri, xml:id, type, fetchtimeout / refPLS 1.0 발음 사전을 불러와 lookup 안의 텍스트에 적용합니다. alias 항목과 alphabet="x-hangul" phoneme 항목을 씁니다. 영문·숫자 표제어는 단어 단위로 맞춥니다.
<token>, <w>role내용을 한 단어로 다룹니다. 앞뒤 글자와 붙어 있으면 띄어 읽습니다. role은 쓰지 않습니다.

속도·높이·음량

속도·높이·음량
요소속성동작
<prosody>rate말하는 속도입니다. 아래 값과 N%는 요청 속도에 곱하는 값이라 바깥 prosody의 rate와 상관없이 정해집니다. +N%, -N%는 지금 속도를 바꿉니다. 0.5–2.0배로 제한합니다.
<prosody>pitch목소리 높이입니다. 아래 값은 요청 높이에서의 수준이고, ±Nst(반음), ±N%, ±NHz는 지금 높이를 바꾸며, NHz는 그 높이에 맞춥니다(NHz 안의 ±Nst·±N%는 그 높이를 기준으로 바꿉니다). ±6반음으로 제한합니다. Hz 값은 합성한 구간의 평균 높이를 재서 맞춥니다.
<prosody>volume음량입니다. 아래 값은 요청 음량에서의 수준이고 ±NdB는 지금 음량을 바꿉니다. silent 안의 ±NdB는 계속 소리가 없고, 이름 값과 default는 다시 소리를 냅니다. 최대 약 +6dB까지 올라갑니다.
<prosody>duration내용을 합성한 뒤 duration(예: 3s) 길이에 맞게 늘이거나 줄입니다(0.5–2.0배). 같은 요소의 rate는 쓰지 않고, duration 안에 duration을 넣을 수 없습니다.

목소리·언어

목소리·언어
요소속성동작
<voice>name, onvoicefailure프리셋 목소리로 바꿉니다. 감정은 항상 neutral입니다. name에 여러 이름을 공백으로 주면 앞에서부터 있는 목소리를 씁니다. 없는 이름은 404(E4301)이고 onvoicefailure="keepexisting"이면 지금 목소리를 유지합니다.
<lang>xml:lang, onlangfailure한국어(ko)·영어(en)로 바꿉니다. 그 밖의 언어는 onlangfailure를 따릅니다. ignoretext면 건너뛰고, 나머지는 앞 언어로 읽습니다.

오디오·마크

오디오·마크
요소속성동작
<audio>src, clipBegin, clipEnd, repeatCount, repeatDur, soundLevel, speed, fetchtimeoutsrc의 오디오를 가져와 그 자리에 넣습니다. 구간 자르기, 반복(repeatCount와 repeatDur를 함께 주면 repeatDur), 음량(±60dB), 재생 속도(25–400%)를 적용합니다. 가져오거나 디코드하지 못하면 400(E4203)이고 안의 대체 텍스트는 읽지 않습니다.
<mark>name그 위치의 시각(초)을 응답의 marks에 담습니다. 소리에는 영향이 없습니다.
3

이름으로 쓰는 값

숫자 대신 쓸 수 있는 값과 실제로 적용되는 크기입니다.

break strength

break strength
값적용
none0초
x-weak0.1초
weak0.25초
medium0.5초(기본)
strong0.75초
x-strong1초

prosody rate

prosody rate
값적용
x-slow0.6배
slow0.8배
medium1배
fast1.25배
x-fast1.5배

prosody pitch

prosody pitch
값적용
x-low−4반음
low−2반음
medium0
high+2반음
x-high+4반음

prosody volume

prosody volume
값적용
silent소리 없음
x-soft−12dB
soft−6dB
medium0dB
loud+3dB
x-loud+6dB
4

say-as interpret-as

W3C say-as 값 6개를 지원합니다. 결과는 문서 언어(ko·en)를 따릅니다.

say-as interpret-as
값format · detail입력읽는 소리
cardinalformat: 소수점 문자, detail: 자릿수 구분 문자1,234,500 · -3.5백이십삼만 사천오백 · 마이너스 삼 쩜 오
ordinal—1 · 21 · 123첫 번째 · 스물한 번째 · 백이십삼 번째
charactersformat: characters(기본)·glyphs, detail: 묶음 길이(예: 3 1 2)AB12에이 비 일 이
dateformat: mdy, dmy, ymd, md, dm, ym, my, d, m, y(구분자 - / .)2026-10-23 (ymd)이천이십육년 시월 이십삼일
timeformat: hms24, hms12(AM·PM). 초는 소수 가능15:30 · 3:30 PM십오시 삼십분 · 오후 세시 삼십분
telephoneformat: 국가 번호(읽지 않음)1588-1234일 오 팔 팔, 일 이 삼 사
  • 위 6개가 아닌 interpret-as는 400(E4201)입니다. 모르는 format은 format 없이 읽고, 내용과 맞지 않는 format도 format 없이 다시 읽습니다. 그래도 맞지 않으면 일반 텍스트로 읽습니다. 모르는 format과 일반 텍스트로 읽은 경우는 경고(warnings)로 알려 줍니다.
  • cardinal은 한자어 숫자로 읽습니다. 정수 바로 뒤에 개·명·시·번 같은 단위가 오면 일반 텍스트와 같은 규칙으로 읽습니다(<say-as interpret-as="cardinal">3</say-as>개 → 세개).
  • ordinal은 차례(세 번째)로 읽습니다. 99까지는 고유어, 100부터는 끝자리 1–9만 고유어입니다(백한 번째, 백십 번째). "3번 출구"처럼 번호를 붙이는 말은 ordinal이 아니라 일반 텍스트로 씁니다.
  • characters는 악센트가 있는 라틴 글자를 기저 글자와 부호로(ö → 오 움라우트), 그리스·키릴 글자를 이름으로(α → 알파) 읽고, 읽는 법이 없는 글자도 빼지 않습니다. 0으로 시작하는 date 연도는 적힌 대로 읽습니다(02 → 공이년).
5

예시

그대로 복사해 text에 넣을 수 있습니다. example.com 주소는 직접 올린 파일 주소로 바꾸세요.

문단·문장·쉼

문장 사이와 문단 사이의 기본 쉼, break time으로 정한 쉼입니다.

xml
<speak><p><s>안녕하세요.</s><s>프로소디 안내 방송입니다.</s></p><p>잠시 후<break time="1s"/>열차가 도착합니다.</p></speak>

break strength

none부터 x-strong까지 쉼 길이를 비교합니다.

xml
<speak>하나<break strength="none"/>둘<break strength="x-weak"/>셋<break strength="weak"/>넷<break strength="medium"/>다섯<break strength="strong"/>여섯<break strength="x-strong"/>일곱</speak>

say-as: 금액과 순서

cardinal과 ordinal입니다.

xml
<speak>결제 금액은 <say-as interpret-as="cardinal">1,234,500</say-as>원입니다. <say-as interpret-as="ordinal">3</say-as> 정류장에서 내리세요.</speak>

say-as: 코드와 전화번호

characters는 한 글자씩, telephone은 번호를 묶어 읽습니다.

xml
<speak>예약 코드는 <say-as interpret-as="characters">AB12</say-as>입니다. 문의는 <say-as interpret-as="telephone">1588-1234</say-as>로 해 주세요.</speak>

say-as: 날짜와 시각

date format="ymd"와 time format="hms12"입니다.

xml
<speak>출발일은 <say-as interpret-as="date" format="ymd">2026-10-23</say-as>, 출발 시각은 <say-as interpret-as="time" format="hms12">3:30 PM</say-as>입니다.</speak>

say-as: 편명·좌석

표준 값을 이어 붙여 편명과 좌석을 읽습니다.

xml
<speak><say-as interpret-as="characters">KE</say-as><say-as interpret-as="cardinal">123</say-as>편 탑승을 시작합니다. 좌석 <say-as interpret-as="cardinal">23</say-as><say-as interpret-as="characters">A</say-as> 손님은 탑승구로 와 주세요.</speak>

sub: 약어

약어 대신 alias를 읽습니다.

xml
<speak><sub alias="세계보건기구">WHO</sub> 기준에 따라 안내합니다.</speak>

phoneme: 한글 발음 표기

x-hangul로 적은 소리대로 읽습니다.

xml
<speak><phoneme alphabet="x-hangul" ph="실라">신라</phoneme> 호텔 앞 정류장입니다.</speak>

lexicon·lookup: 발음 사전

PLS 사전 파일의 표제어를 lookup 안에서 바꿔 읽습니다.

xml
<speak><lexicon xml:id="trains" uri="https://example.com/lexicons/prosody.pls"/><lookup ref="trains">KTX와 SRT 승강장은 지하 1층입니다.</lookup></speak>

prosody rate

느리게, 보통, 빠르게 읽습니다.

xml
<speak><prosody rate="slow">천천히 말합니다.</prosody> 보통 속도입니다. <prosody rate="fast">빠르게 말합니다.</prosody></speak>

prosody pitch

반음 단위와 Hz 값으로 높이를 바꿉니다.

xml
<speak><prosody pitch="-3st">낮은 목소리입니다.</prosody> 보통 높이입니다. <prosody pitch="+3st">높은 목소리입니다.</prosody> <prosody pitch="250Hz">이백오십 헤르츠에 맞춥니다.</prosody></speak>

prosody volume

작게, 보통, 크게 읽습니다.

xml
<speak><prosody volume="soft">작게 말합니다.</prosody> 보통 크기입니다. <prosody volume="loud">크게 말합니다.</prosody></speak>

prosody duration

한 문장을 정확히 3초에 맞춥니다.

xml
<speak>다음 문장을 삼 초에 맞춥니다. <prosody duration="3s">이 문장은 정확히 삼 초 동안 읽습니다.</prosody></speak>

voice: 목소리 바꾸기

요청 목소리(나나)에서 윤기로 바꿨다가 돌아옵니다.

xml
<speak>안녕하세요, 나나입니다. <voice name="윤기">저는 윤기입니다.</voice> 다시 나나입니다.</speak>

lang: 영어 섞기

한국어 문장 안에서 영어로 바꿔 읽습니다.

xml
<speak>이번 역은 <lang xml:lang="en-US">Seoul Station</lang>입니다.</speak>

audio: 효과음 넣기

알림음을 넣고, 두 번 반복하면서 6dB 낮춥니다.

xml
<speak>안내를 시작합니다.<audio src="https://example.com/sounds/chime.wav"/>열차가 들어오고 있습니다.<audio src="https://example.com/sounds/chime.wav" repeatCount="2" soundLevel="-6dB"/>안전선 밖으로 물러나 주세요.</speak>

mark: 위치 받기

응답의 marks에 각 마크의 시각이 담깁니다.

xml
<speak><mark name="start"/>첫 번째 안내입니다.<break time="700ms"/><mark name="second"/>두 번째 안내입니다.<mark name="end"/></speak>

startmark·endmark: 일부만 합성

두 마크 사이만 합성합니다.

xml
<speak startmark="a" endmark="b">이 부분은 읽지 않습니다. <mark name="a"/>이 부분만 읽습니다.<mark name="b"/> 여기도 읽지 않습니다.</speak>

효과 없이 읽는 태그

emphasis, prosody contour·range는 오류 없이 내용만 읽습니다.

xml
<speak><emphasis level="strong">중요한</emphasis> 안내입니다. <prosody contour="(0%,+20Hz) (100%,-10Hz)" range="x-high">이 문장은 보통처럼 읽습니다.</prosody></speak>
6

마크·경고 응답

mark 위치와 경고(warnings)는 응답 방식마다 아래 자리에 담깁니다.

마크·경고 응답
응답위치
Standard, JSON 응답"marks": [{ "name": "start", "time": 0 }], "warnings": ["..."]
Standard, rawData: trueX-Prosody-Marks: [...], X-Prosody-Warnings: [...]
StreamingTrailer X-Prosody-Marks: [...], X-Prosody-Warnings: [...]
WebSocket{ "type": "completed", "marks": [...], "warnings": [...] }
  • 경고(warnings)는 합성은 했지만 문서와 다르게 처리한 점입니다. 범위를 넘은 값을 한도로 맞춤(prosody rate·pitch·volume, break 10초, audio soundLevel·speed·fetchtimeout, Hz 피치 ±6반음, duration 0.5–2배), say-as 내용·format 불일치, 지원하지 않는 언어·phoneme 표기가 해당합니다. 예: line 2: prosody rate "300%" is outside 0.5–2× of normal speed; read at 2×. 경고가 없으면 필드와 헤더가 없습니다.
  • 헤더와 trailer 값은 JSON이며 한글 같은 비ASCII 글자는 \uXXXX로 이스케이프합니다.
  • 브라우저 fetch는 HTTP trailer를 읽지 못합니다. 브라우저에서 스트리밍 마크가 필요하면 WebSocket을 쓰세요.
  • 쉼·오디오·문장 경계에 있는 마크의 시각은 정확하고, 문장 안의 마크는 글자 위치로 추정합니다. 스트리밍은 합성이 끝날 때 한 번에 전달합니다.
7

오디오·사전 가져오기

audio의 src와 lexicon의 uri를 서버가 직접 가져옵니다.

  • src는 공개 https·http 주소 또는 data: URI만 받습니다. 사설망·loopback·링크로컬 주소와 그리로 가는 리다이렉트는 막습니다. 리다이렉트는 3번까지 따라갑니다.
  • WAV(PCM·float·μ-law·A-law), AU, 헤더 없는 8kHz G.711(audio/basic, audio/x-alaw-basic), MP3, OGG, FLAC, AAC·M4A, WebM, AIFF를 읽습니다.
  • 자원 하나는 10MiB, 기본 5초(fetchtimeout으로 최대 30초) 안에 받아야 하고, 한 요청의 모든 자원은 60초 안에 받아야 합니다. 가져온 파일은 캐시하지 않습니다.
  • lexicon(PLS)도 같은 규칙으로 가져오며, 실패하면 400(E4201)입니다.
8

효과 없음·지원하지 않음

  • emphasis, prosody contour·range는 받지만 효과가 없습니다. contour가 있으면 같은 요소의 pitch도 적용하지 않습니다.
  • phoneme의 IPA 등 x-hangul이 아닌 표기는 쓰지 않고 내용 텍스트를 읽으며, 경고로 알려 줍니다.
  • voice의 gender, age, variant, languages로는 목소리를 고르지 않습니다. name을 쓰세요. 속성이 하나도 없는 voice는 400입니다.
  • SSML 1.0식 숫자 값(rate="1.5", volume="80")은 받지 않습니다(400). 150%, +3dB처럼 SSML 1.1 값을 쓰세요.
  • DTD 엔티티는 확장하지 않습니다. 선언한 엔티티를 쓰면 400입니다.
  • SSML에 없는 요소·속성(<emphsis>, strenght="weak" 같은 오타)은 400(E4201)입니다. details에 줄 번호와 가장 비슷한 이름을 알려 줍니다(예: line 1: unknown element <emphsis>; did you mean <emphasis>?).
  • amazon:effect, mstts:express-as처럼 다른 네임스페이스(접두사)의 요소·속성은 무시하고 내용을 읽습니다. 다른 TTS용 SSML을 옮겨 와도 실패하지 않습니다.
9

알아 둘 점

10

한도

문서 하나에 적용합니다. 넘으면 400입니다.

한도
항목한도
요소4,096개, 중첩 32단계
합성 구간256개
마크200개, 이름 128바이트
쉼 합계120초
audio32개, 파일 180초, 클립 120초, 합계 300초
lexicon8개, 항목 합계 5,000개
읽을 텍스트say-as·사전 적용 뒤 글자 수 한도의 8배
11

오류

SSML 때문에 생기는 오류입니다. 그 밖의 오류는 일반 DIVE 요청과 같습니다.

오류
HTTP코드언제
400E4201XML 문법 오류, SSML에 없는 요소·속성·interpret-as, 지원하지 않는 값, 한도 초과, lexicon을 가져오거나 읽지 못함, 소리가 나지 않는 문서
400E4203audio를 가져오거나 디코드하지 못함, 오디오 길이 한도 초과
403E4101Pro·Enterprise가 아닌 요금제에서 SSML 요청
404E4301voice name에 없는 목소리