추가 기능 · SSML
SSML
DIVE 요청의 text에 SSML 1.1 문서를 넣어 쉼, 읽는 방식, 속도·높이·음량, 목소리, 효과음, 위치 마크를 조절합니다.
SSML로 합성하기
일반 텍스트 요청과 같은 엔드포인트·필드를 쓰고 text만 SSML 문서로 보냅니다.
지원 엔드포인트
https://api-console.humelo.net/api/v1/divehttps://api-console.humelo.net/api/v1/dive/streamwss://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는 서버 환경변수에 보관하고 요청 헤더로만 전달하세요.
X-API-Key: {YOUR_API_KEY}
Content-Type: application/json요청 예시
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으로 늘어난 길이는 과금하지 않습니다.
지원 요소
아래 요소와 속성을 해석합니다. 표에 없는 요소는 태그를 무시하고 내용을 읽습니다.
문서 구조
| 요소 | 속성 | 동작 |
|---|---|---|
<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, strength | time(예: 500ms, 1.5s, 최대 10초) 또는 strength만큼 쉽니다. 속성이 없으면 medium입니다. 같은 자리의 문장·문단 쉼을 대신합니다. |
읽는 방식
| 요소 | 속성 | 동작 |
|---|---|---|
<say-as> | interpret-as, format, detail | 숫자·글자·날짜·시각·전화번호를 읽는 방식을 정합니다. 아래 say-as 표의 6개 값을 지원합니다. |
<sub> | alias | 내용 대신 alias를 읽습니다. 약어·기호에 씁니다. |
<phoneme> | alphabet, ph | alphabet="x-hangul"(기본)이면 ph에 적은 한글 표기대로 읽습니다. ipa 등 다른 표기법은 효과 없이 내용을 읽습니다. |
<lexicon>, <lookup> | uri, xml:id, type, fetchtimeout / ref | PLS 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, fetchtimeout | src의 오디오를 가져와 그 자리에 넣습니다. 구간 자르기, 반복(repeatCount와 repeatDur를 함께 주면 repeatDur), 음량(±60dB), 재생 속도(25–400%)를 적용합니다. 가져오거나 디코드하지 못하면 400(E4203)이고 안의 대체 텍스트는 읽지 않습니다. |
<mark> | name | 그 위치의 시각(초)을 응답의 marks에 담습니다. 소리에는 영향이 없습니다. |
이름으로 쓰는 값
숫자 대신 쓸 수 있는 값과 실제로 적용되는 크기입니다.
break strength
| 값 | 적용 |
|---|---|
none | 0초 |
x-weak | 0.1초 |
weak | 0.25초 |
medium | 0.5초(기본) |
strong | 0.75초 |
x-strong | 1초 |
prosody rate
| 값 | 적용 |
|---|---|
x-slow | 0.6배 |
slow | 0.8배 |
medium | 1배 |
fast | 1.25배 |
x-fast | 1.5배 |
prosody pitch
| 값 | 적용 |
|---|---|
x-low | −4반음 |
low | −2반음 |
medium | 0 |
high | +2반음 |
x-high | +4반음 |
prosody volume
| 값 | 적용 |
|---|---|
silent | 소리 없음 |
x-soft | −12dB |
soft | −6dB |
medium | 0dB |
loud | +3dB |
x-loud | +6dB |
say-as interpret-as
W3C say-as 값 6개를 지원합니다. 결과는 문서 언어(ko·en)를 따릅니다.
| 값 | format · detail | 입력 | 읽는 소리 |
|---|---|---|---|
cardinal | format: 소수점 문자, detail: 자릿수 구분 문자 | 1,234,500 · -3.5 | 백이십삼만 사천오백 · 마이너스 삼 쩜 오 |
ordinal | — | 1 · 21 · 123 | 첫 번째 · 스물한 번째 · 백이십삼 번째 |
characters | format: characters(기본)·glyphs, detail: 묶음 길이(예: 3 1 2) | AB12 | 에이 비 일 이 |
date | format: mdy, dmy, ymd, md, dm, ym, my, d, m, y(구분자 - / .) | 2026-10-23 (ymd) | 이천이십육년 시월 이십삼일 |
time | format: hms24, hms12(AM·PM). 초는 소수 가능 | 15:30 · 3:30 PM | 십오시 삼십분 · 오후 세시 삼십분 |
telephone | format: 국가 번호(읽지 않음) | 1588-1234 | 일 오 팔 팔, 일 이 삼 사 |
- 모르는
format이나 내용과 맞지 않는format은format없이 같은interpret-as로 읽고, 그래도 맞지 않거나 모르는interpret-as면 일반 텍스트로 읽습니다. cardinal은 한자어 숫자로 읽습니다. 정수 바로 뒤에 개·명·시·번 같은 단위가 오면 일반 텍스트와 같은 규칙으로 읽습니다(<say-as interpret-as="cardinal">3</say-as>개→ 세개).ordinal은 차례(세 번째)로 읽습니다. 99까지는 고유어, 100부터는 끝자리 1–9만 고유어입니다(백한 번째, 백십 번째). "3번 출구"처럼 번호를 붙이는 말은ordinal이 아니라 일반 텍스트로 씁니다.characters는 악센트가 있는 라틴 글자를 기저 글자와 부호로(ö → 오 움라우트), 그리스·키릴 글자를 이름으로(α → 알파) 읽고, 읽는 법이 없는 글자도 빼지 않습니다. 0으로 시작하는date연도는 적힌 대로 읽습니다(02 → 공이년).
예시
그대로 복사해 text에 넣을 수 있습니다. example.com 주소는 직접 올린 파일 주소로 바꾸세요.
문단·문장·쉼
문장 사이와 문단 사이의 기본 쉼, break time으로 정한 쉼입니다.
<speak><p><s>안녕하세요.</s><s>프로소디 안내 방송입니다.</s></p><p>잠시 후<break time="1s"/>열차가 도착합니다.</p></speak>break strength
none부터 x-strong까지 쉼 길이를 비교합니다.
<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입니다.
<speak>결제 금액은 <say-as interpret-as="cardinal">1,234,500</say-as>원입니다. <say-as interpret-as="ordinal">3</say-as> 정류장에서 내리세요.</speak>say-as: 코드와 전화번호
characters는 한 글자씩, telephone은 번호를 묶어 읽습니다.
<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"입니다.
<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: 편명·좌석
표준 값을 이어 붙여 편명과 좌석을 읽습니다.
<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를 읽습니다.
<speak><sub alias="세계보건기구">WHO</sub> 기준에 따라 안내합니다.</speak>phoneme: 한글 발음 표기
x-hangul로 적은 소리대로 읽습니다.
<speak><phoneme alphabet="x-hangul" ph="실라">신라</phoneme> 호텔 앞 정류장입니다.</speak>lexicon·lookup: 발음 사전
PLS 사전 파일의 표제어를 lookup 안에서 바꿔 읽습니다.
<speak><lexicon xml:id="trains" uri="https://example.com/lexicons/prosody.pls"/><lookup ref="trains">KTX와 SRT 승강장은 지하 1층입니다.</lookup></speak>prosody rate
느리게, 보통, 빠르게 읽습니다.
<speak><prosody rate="slow">천천히 말합니다.</prosody> 보통 속도입니다. <prosody rate="fast">빠르게 말합니다.</prosody></speak>prosody pitch
반음 단위와 Hz 값으로 높이를 바꿉니다.
<speak><prosody pitch="-3st">낮은 목소리입니다.</prosody> 보통 높이입니다. <prosody pitch="+3st">높은 목소리입니다.</prosody> <prosody pitch="250Hz">이백오십 헤르츠에 맞춥니다.</prosody></speak>prosody volume
작게, 보통, 크게 읽습니다.
<speak><prosody volume="soft">작게 말합니다.</prosody> 보통 크기입니다. <prosody volume="loud">크게 말합니다.</prosody></speak>prosody duration
한 문장을 정확히 3초에 맞춥니다.
<speak>다음 문장을 삼 초에 맞춥니다. <prosody duration="3s">이 문장은 정확히 삼 초 동안 읽습니다.</prosody></speak>voice: 목소리 바꾸기
요청 목소리(나나)에서 윤기로 바꿨다가 돌아옵니다.
<speak>안녕하세요, 나나입니다. <voice name="윤기">저는 윤기입니다.</voice> 다시 나나입니다.</speak>lang: 영어 섞기
한국어 문장 안에서 영어로 바꿔 읽습니다.
<speak>이번 역은 <lang xml:lang="en-US">Seoul Station</lang>입니다.</speak>audio: 효과음 넣기
알림음을 넣고, 두 번 반복하면서 6dB 낮춥니다.
<speak>안내를 시작합니다.<audio src="https://example.com/sounds/chime.wav"/>열차가 들어오고 있습니다.<audio src="https://example.com/sounds/chime.wav" repeatCount="2" soundLevel="-6dB"/>안전선 밖으로 물러나 주세요.</speak>mark: 위치 받기
응답의 marks에 각 마크의 시각이 담깁니다.
<speak><mark name="start"/>첫 번째 안내입니다.<break time="700ms"/><mark name="second"/>두 번째 안내입니다.<mark name="end"/></speak>startmark·endmark: 일부만 합성
두 마크 사이만 합성합니다.
<speak startmark="a" endmark="b">이 부분은 읽지 않습니다. <mark name="a"/>이 부분만 읽습니다.<mark name="b"/> 여기도 읽지 않습니다.</speak>효과 없이 읽는 태그
emphasis, prosody contour·range는 오류 없이 내용만 읽습니다.
<speak><emphasis level="strong">중요한</emphasis> 안내입니다. <prosody contour="(0%,+20Hz) (100%,-10Hz)" range="x-high">이 문장은 보통처럼 읽습니다.</prosody></speak>마크 응답
mark 위치는 응답 방식마다 아래 자리에 담깁니다.
| 응답 | 위치 |
|---|---|
| Standard, JSON 응답 | "marks": [{ "name": "start", "time": 0 }] |
Standard, rawData: true | X-Prosody-Marks: [...] |
| Streaming | Trailer X-Prosody-Marks: [...] |
| WebSocket | { "type": "completed", "marks": [...] } |
- 헤더와 trailer 값은 JSON이며 한글 같은 비ASCII 글자는
\uXXXX로 이스케이프합니다. - 브라우저
fetch는 HTTP trailer를 읽지 못합니다. 브라우저에서 스트리밍 마크가 필요하면 WebSocket을 쓰세요. - 쉼·오디오·문장 경계에 있는 마크의 시각은 정확하고, 문장 안의 마크는 글자 위치로 추정합니다. 스트리밍은 합성이 끝날 때 한 번에 전달합니다.
오디오·사전 가져오기
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)입니다.
효과 없음·지원하지 않음
emphasis,prosody contour·range,phoneme의 IPA 표기는 받지만 효과가 없습니다.contour가 있으면 같은 요소의pitch도 적용하지 않습니다.voice의gender,age,variant,languages로는 목소리를 고르지 않습니다.name을 쓰세요. 속성이 하나도 없는voice는 400입니다.- SSML 1.0식 숫자 값(
rate="1.5",volume="80")은 받지 않습니다(400).150%,+3dB처럼 SSML 1.1 값을 쓰세요. - DTD 엔티티는 확장하지 않습니다. 선언한 엔티티를 쓰면 400입니다.
- 모르는 요소나 다른 네임스페이스의 요소는 태그만 무시하고 내용을 읽습니다.
알아 둘 점
- 태그로 문장 중간을 나누면(속도·높이·음량·목소리를 바꾸거나
break·audio를 넣으면) 각 부분을 따로 합성합니다. 문장 중간에서 끝나는 부분은 이어지는 억양으로 읽지만, 바꾸는 단위를 문장으로 잡는 편이 가장 자연스럽습니다. - 한국어 조사가 떨어지지 않도록
sub,say-as,lookup처럼 소리를 바꾸지 않는 태그는 앞뒤 글자와 붙여 읽습니다(<sub alias="세계보건기구">WHO</sub>는→ "세계보건기구는"). - 단어장(
dictionaryId)은 태그 밖의 일반 텍스트에만 적용하고say-as,sub,phoneme, lexicon으로 바꾼 글자에는 적용하지 않습니다. - 영어(
langen)는 영어 발음을 지원하는 목소리에서만 자연스럽게 들립니다.
한도
문서 하나에 적용합니다. 넘으면 400입니다.
| 항목 | 한도 |
|---|---|
| 요소 | 4,096개, 중첩 32단계 |
| 합성 구간 | 256개 |
| 마크 | 200개, 이름 128바이트 |
| 쉼 합계 | 120초 |
| audio | 32개, 파일 180초, 클립 120초, 합계 300초 |
| lexicon | 8개, 항목 합계 5,000개 |
| 읽을 텍스트 | say-as·사전 적용 뒤 글자 수 한도의 8배 |
오류
SSML 때문에 생기는 오류입니다. 그 밖의 오류는 일반 DIVE 요청과 같습니다.
| HTTP | 코드 | 언제 |
|---|---|---|
| 400 | E4201 | XML 문법 오류, 지원하지 않는 값, 한도 초과, lexicon을 가져오거나 읽지 못함, 소리가 나지 않는 문서 |
| 400 | E4203 | audio를 가져오거나 디코드하지 못함, 오디오 길이 한도 초과 |
| 403 | E4101 | Pro·Enterprise가 아닌 요금제에서 SSML 요청 |
| 404 | E4301 | voice name에 없는 목소리 |