feat: [sound-logo] 아이 목소리 옵션 + 말하기 속도 조절 추가

- 스타일 옵션에 어린이(5~7세), 초등학생(8~12세), 청소년(13~18세) 추가
- 말하기 속도 슬라이더 추가 (매우느리게~매우빠르게 5단계)
- 속도와 스타일을 TTS 프롬프트 지시문으로 조합하여 Gemini API에 전달
- 음성 목록 여성/남성/중성 순서로 정렬
This commit is contained in:
김보곤
2026-03-08 14:05:22 +09:00
parent 64b3ad2b59
commit 0e86636354
2 changed files with 42 additions and 11 deletions

View File

@@ -487,6 +487,7 @@ public function soundLogoTts(Request $request): JsonResponse
'text' => 'required|string|max:200',
'voice_name' => 'nullable|string|max:30',
'voice_style' => 'nullable|string|max:100',
'voice_speed' => 'nullable|integer|min:1|max:5',
]);
$apiKey = config('services.gemini.api_key');
@@ -498,13 +499,29 @@ public function soundLogoTts(Request $request): JsonResponse
$voiceName = $request->voice_name ?: 'Kore';
$voiceStyle = $request->voice_style ?: '';
$voiceSpeed = $request->voice_speed ?: 3;
// 속도 지시문 매핑
$speedDirectives = [
1 => '아주 천천히 또박또박 말해주세요.',
2 => '조금 느린 속도로 말해주세요.',
3 => '', // 보통 — 지시 없음
4 => '조금 빠른 속도로 말해주세요.',
5 => '아주 빠른 속도로 말해주세요.',
];
// TTS에 전달할 텍스트 구성
$ttsText = $request->text;
// 스타일 지시가 있으면 프롬프트로 감싸기
$directives = [];
if ($voiceStyle) {
$ttsText = "{$voiceStyle}: {$ttsText}";
$directives[] = $voiceStyle;
}
if (! empty($speedDirectives[$voiceSpeed])) {
$directives[] = $speedDirectives[$voiceSpeed];
}
$ttsText = $request->text;
if (! empty($directives)) {
$ttsText = implode(' ', $directives)." \n\n{$ttsText}";
}
// 짧은 텍스트는 TTS 모델이 텍스트 생성으로 인식할 수 있으므로 발화 컨텍스트 추가

View File

@@ -369,6 +369,16 @@
</template>
</select>
<!-- 말하기 속도 -->
<div class="sl-param" style="margin-bottom: 8px;">
<div class="sl-param-label">
<span><i class="ri-speed-line"></i> 말하기 속도</span>
<span x-text="voiceSpeedLabels[voiceSpeed - 1]" style="font-size: 10px;"></span>
</div>
<input type="range" class="sl-slider" min="1" max="5" step="1"
x-model.number="voiceSpeed">
</div>
<button class="sl-btn sm primary" style="width:100%;" @click="generateVoice()" :disabled="voiceLoading || !voiceText.trim()">
<template x-if="!voiceLoading">
<span><i class="ri-mic-line"></i> 음성 생성</span>
@@ -1068,23 +1078,24 @@ function soundLogo() {
voiceBuffer: null,
voiceName: 'Kore',
voiceStyle: '',
voiceSpeed: 3, // 1~5 (1=매우느림, 3=보통, 5=매우빠름)
voiceOptions: [
{ value: 'Kore', label: '코어', desc: '단정하고 명확한', gender: '여성' },
{ value: 'Aoede', label: '아오이데', desc: '산뜻하고 가벼운', gender: '여성' },
{ value: 'Leda', label: '레다', desc: '젊고 발랄한', gender: '여성' },
{ value: 'Achernar', label: '아케르나르', desc: '부드럽고 섬세한', gender: '여성' },
{ value: 'Sulafat', label: '술라파트', desc: '따뜻하고 포근한', gender: '여성' },
{ value: 'Sadachbia', label: '사다키비아', desc: '활기차고 생동감', gender: '여성' },
{ value: 'Vindemiatrix', label: '빈데미아트릭스', desc: '부드럽고 차분한', gender: '여성' },
{ value: 'Puck', label: '퍽', desc: '밝고 활발한', gender: '남성' },
{ value: 'Charon', label: '카론', desc: '정보적이고 안정적', gender: '남성' },
{ value: 'Fenrir', label: '펜리르', desc: '에너지 넘치는', gender: '남성' },
{ value: 'Orus', label: '오루스', desc: '무게감 있는', gender: '남성' },
{ value: 'Perseus', label: '페르세우스', desc: '차분하고 침착한', gender: '남성' },
{ value: 'Zephyr', label: '제피르', desc: '밝고 경쾌한', gender: '중성' },
{ value: 'Achernar', label: '아케르나르', desc: '부드럽고 섬세한', gender: '여성' },
{ value: 'Gacrux', label: '가크룩스', desc: '성숙하고 중후한', gender: '남성' },
{ value: 'Sulafat', label: '술라파트', desc: '따뜻하고 포근한', gender: '여성' },
{ value: 'Achird', label: '아키르드', desc: '친근하고 다정한', gender: '남성' },
{ value: 'Vindemiatrix', label: '빈데미아트릭스', desc: '부드럽고 차분한', gender: '여성' },
{ value: 'Sadachbia', label: '사다키비아', desc: '활기차고 생동감', gender: '여성' },
{ value: 'Algieba', label: '알기에바', desc: '매끄럽고 세련된', gender: '남성' },
{ value: 'Zephyr', label: '제피르', desc: '밝고 경쾌한', gender: '중성' },
],
voiceStyleOptions: [
{ value: '', label: '기본 스타일' },
@@ -1094,9 +1105,11 @@ function soundLogo() {
{ value: '힘 있고 당당하게', label: '힘 있고 당당하게' },
{ value: '귀엽고 사랑스럽게', label: '귀엽고 사랑스럽게' },
{ value: '진지하고 전문적으로', label: '진지하고 전문적으로' },
{ value: '느리고 또박또박', label: '느리고 또박또박' },
{ value: '빠르고 신나게', label: '빠르고 신나게' },
{ value: '어린 아이의 목소리로', label: '어린 아이 (5~7세)' },
{ value: '초등학생 아이의 밝은 목소리로', label: '초등학생 (8~12세)' },
{ value: '10대 청소년의 목소리로', label: '청소년 (13~18세)' },
],
voiceSpeedLabels: ['매우 느리게', '느리게', '보통', '빠르게', '매우 빠르게'],
aiQuickPrompts: [
'밝고 미래적인 IT 기업 로고',
@@ -1669,6 +1682,7 @@ function soundLogo() {
text: this.voiceText,
voice_name: this.voiceName,
voice_style: this.voiceStyle,
voice_speed: this.voiceSpeed,
}),
});