Developer Guide

SeaVoice UI Tabs Implementation Guide

Hướng dẫn chi tiết thiết kế và triển khai giao diện 4 Tab chức năng trong SeaVoice Studio (TTS, Clone, Design, Registry)

Hướng Dẫn Triển Khai Các Tab Trong SeaVoice Studio

Tài liệu kỹ thuật chi tiết dành cho kỹ sư Frontend và Fullstack để xây dựng và tích hợp giao diện Studio quản trị âm thanh SeaVoice Studio (bao gồm 4 Tabs: Sinh giọng TTS, Nhân bản giọng, Thiết kế giọng và Quản lý danh mục giọng) vào bất kỳ dự án React / Next.js / TanStack Start nào.


1. Kiến Trúc Tổng Thể Studio (AudioGeneratePanel)

Giao diện Studio được đóng gói trong component container AudioGeneratePanel, điều phối trạng thái và luồng dữ liệu giữa 4 tabs độc lập thông qua hệ thống State trung tâm:

1.1. Luồng Trải Nghiệm Người Dùng (UX Flow)

  1. Khởi tạo: Mặc định mở Tab 1 (Sinh giọng) với một giọng mặc định trong danh mục hoặc giọng được truyền qua props (selectedVoice).
  2. Tạo giọng mới: Khi người dùng vào Tab 2 (Nhân bản) hoặc Tab 3 (Thiết kế) để tạo một giọng mới thành công, hệ thống tự động:
    • Ghi nhận VoiceRegistryRow mới tạo vào state selectedVoice.
    • Tự động chuyển active tab quay lại Tab 1 (Sinh giọng).
    • Nạp ngay giọng vừa tạo vào bộ chọn để người dùng có thể gõ nội dung đọc thử tức thì.
  3. Chọn từ thư viện: Khi người dùng duyệt Tab 4 (Danh mục giọng) và ấn "Sử dụng giọng này", hệ thống chuyển ngay về Tab 1 với giọng đã chọn.

2. Chi Tiết Triển Khai Tab 1: Sinh Giọng (TTS Generation)

Component: TtsForm Mục đích: Cho phép người dùng nhập văn bản, chèn các biểu cảm tự nhiên, tinh chỉnh thông số âm thanh và sinh file đọc tức thì.

2.1. Cấu Trúc Giao Diện & Điều Khiển

+-----------------------------------------------------------------------------+
|  [Chọn giọng đọc ▾] (Giọng của tôi / Giọng công đồng / Preset)              |
|  [Đoạn văn bản cần đọc ...................................................] |
|  [Thẻ cảm xúc: [cười] [thở dài] [thì thầm] [vỗ tay] [ngáp] [khóc] ...]      |
|                                                                             |
|  [Tốc độ đọc: 1.0x (0.5x - 2.0x)]   [Số bước Denoising: 32 (10 - 64)]       |
|  [Ngôn ngữ: Tiếng Việt ▾]           [Ước tính phí: 120 ký tự = 120 VNĐ]     |
|                                                                             |
|  [ ▶ Nút Sinh Giọng Nói (TTS) ]                                             |
|                                                                             |
|  [ Audio Player Bar: Sóng âm thanh | Play/Pause | Tải về .wav | Gắn vào shot ]|
+-----------------------------------------------------------------------------+

2.2. Các Tính Năng Kỹ Thuật Trọng Tâm

A. Bộ Chèn Thẻ Cảm Xúc Phi Ngôn Ngữ (EmotionBadgeSelector)

Cho phép người dùng click vào các thẻ cảm xúc để chèn mã tag vào đúng vị trí con trỏ (cursor caret) trong ô textarea:

const handleInsertEmotion = (emotion: string) => {
  const textarea = textareaRef.current;
  if (!textarea) {
    setText((prev) => `${prev} ${emotion} `);
    return;
  }

  const start = textarea.selectionStart;
  const end = textarea.selectionEnd;
  const currentText = text;

  // Chèn thẻ cảm xúc vào giữa vị trí con trỏ đang đứng
  const newText =
    currentText.substring(0, start) +
    ` ${emotion} ` +
    currentText.substring(end);

  setText(newText);

  // Phục hồi vị trí con trỏ ngay sau thẻ vừa chèn
  setTimeout(() => {
    textarea.focus();
    const newCursorPos = start + emotion.length + 2;
    textarea.setSelectionRange(newCursorPos, newCursorPos);
  }, 10);
};

B. Bộ Tính Phí Trực Quan (Realtime Billing Counter)

  • Tính toán trực tiếp số ký tự văn bản (text.trim().length).
  • Hiển thị ước tính số VNĐ hoặc credits bị trừ: 1 ký tự = 1 VNĐ.

C. Tham Số Điều Khiển Suy Luận GPU

  • speed (0.5 - 2.0): Thanh trượt tốc độ phát thanh.
  • numStep (10 - 64, mặc định 32): Tùy chỉnh chất lượng âm thanh khử nhiễu.
  • duration (tùy chọn): Khống chế thời lượng file audio (giây) nếu cần khớp timeline video.

3. Chi Tiết Triển Khai Tab 2: Nhân Bản Giọng (Voice Cloning)

Component: VoiceCloneForm Mục đích: Cho phép người dùng tải lên mẫu giọng thật (ref_audio) để tạo ra một Voice ID độc quyền.

3.1. Luồng Xử Lý Dữ Liệu Tải Lên (Audio Upload Pipeline)

3.2. Form Fields & Validation Rules

  • File âm thanh mẫu (ref_audio):
    • Dung lượng: Tối đa 10MB.
    • Định dạng: .wav, .mp3, .m4a, .ogg.
    • Khuyến nghị: 5 - 15 giây, một người nói, không nhạc nền, không tạp âm.
  • Tên giọng đọc (name): Tự động trích xuất từ tên file tải lên (loại bỏ ký tự gạch dưới/đuôi mở rộng) nếu người dùng chưa nhập.
  • Giới tính (gender): male | female | unspecified.
  • Văn bản đối soát (refText) (Không bắt buộc nhưng khuyến khích): Chép lại chính xác nội dung câu nói trong đoạn file mẫu giúp mô hình nhận diện âm vị và phát âm chuẩn hơn 40%.
  • Câu đọc thử nghiệm (testText): Văn bản để sinh ngay một đoạn đọc thử kiểm tra chất lượng bản sao.

4. Chi Tiết Triển Khai Tab 3: Thiết Kế Giọng (Voice Design)

Component: VoiceDesignForm Mục đích: Tạo ra giọng nói hoàn toàn mới bằng cách miêu tả các đặc tính âm học tự nhiên thông qua bộ chọn Tag Chips và Presets có sẵn.

4.1. Hệ Thống Thuộc Tính Âm Học Chuẩn (Taxonomy Chips)

Giao diện chia các thuộc tính thành 5 nhóm thẻ để người dùng click chọn:

Nhóm Thuộc TínhCác Giá Trị Hỗ TrợCơ Chế Chọn
Giới tính (Gender)female (Nữ), male (Nam)Đơn chọn (Single select)
Độ tuổi (Age)child, teenager, young adult, middle-aged, elderlyĐơn chọn trong nhóm
Cao độ (Pitch)very low pitch, low pitch, moderate pitch, high pitch, very high pitchĐơn chọn trong nhóm
Phong cách (Style)whisper (Thì thầm / ASMR)Bật / Tắt (Toggle)
Ngữ điệu (Accent)american accent, british accent, japanese accent, chinese accent, v.v.Đơn chọn trong nhóm

4.2. Logic Đảm Bảo Tính Nhất Quán (Tag Mutual Exclusivity)

Khi người dùng click một thẻ thuộc tính, hệ thống tự động loại bỏ các thẻ cũ cùng nhóm trong chuỗi instruct:

const handleToggleTag = (tagId: string, category: string) => {
  let currentTokens = instruct.split(/[,]+/).map(t => t.trim().toLowerCase());
  
  if (currentTokens.includes(tagId)) {
    // Nếu đã chọn thì gỡ bỏ
    currentTokens = currentTokens.filter(t => t !== tagId);
  } else {
    // Lấy danh sách toàn bộ tag cùng danh mục (ví dụ cùng là độ tuổi)
    const sameCategoryTagIds = SEAVOICE_TAXONOMY
      .filter(t => t.category === category)
      .map(t => t.id.toLowerCase());

    // Loại bỏ các tag cùng danh mục trước khi thêm tag mới
    currentTokens = currentTokens.filter(t => !sameCategoryTagIds.includes(t));
    currentTokens.push(tagId);
  }

  setInstruct(currentTokens.join(', '));
};

4.3. Whitelist Sanitization (Kiểm Tra Từ Khóa Hợp Lệ)

Để tránh lỗi HTTP 400 Bad Request từ SeaVoice API, component tích hợp hook useMemo chạy hàm sanitizeVoiceDesignInstruct để cảnh báo ngay trên UI nếu người dùng nhập các từ khóa lạ không nằm trong danh mục hỗ trợ.


5. Chi Tiết Triển Khai Tab 4: Quản Lý Danh Mục Giọng (Voice Registry)

Component: VoiceRegistryManager Mục đích: Cung cấp trung tâm quản lý tất cả các giọng nói của Team và khám phá các giọng nói dùng chung từ cộng đồng.

5.1. Phân Loại Bộ Lọc (Filter Categories)

  1. Tất cả (all): Hiển thị toàn bộ giọng của team hiện tại + các giọng công cộng đã được duyệt trên toàn nền tảng.
  2. Của tôi / Team (mine): Chỉ hiển thị các giọng do chính team tạo ra (có quyền sửa tên, xóa, yêu cầu chia sẻ công khai).
  3. Cộng đồng (public): Danh sách các giọng nói do cộng đồng chia sẻ đã qua kiểm duyệt (publicStatus === 'approved').

5.2. Thẻ Hiển Thị Giọng (VoiceCard)

Mỗi giọng đọc được hiển thị dưới dạng Card trực quan bao gồm:

  • Thông tin nhận diện: Tên giọng, Avatar giới tính, Voice ID 22 ký tự, ngày tạo.
  • Trình nghe thử tích hợp: Nút Play/Pause mini phát đoạn âm thanh mẫu (sample_url).
  • Huy hiệu kiểm duyệt (Moderation Badge):
    • Riêng tư (Màu xám): Giọng chỉ trong nội bộ team.
    • Đang chờ duyệt (Màu vàng): Đã gửi yêu cầu chia sẻ công khai.
    • Đã duyệt công khai (Màu xanh lá): Mọi người dùng trên hệ thống đều có thể sử dụng.
  • Các thao tác nghiệp vụ:
    • Sử dụng: Kích hoạt onSelectVoice(voice) và chuyển ngay về Tab 1.
    • Chia sẻ công khai / Thu hồi chia sẻ: Gửi hoặc hủy yêu cầu kiểm duyệt.
    • Xóa giọng: Xóa vĩnh viễn khỏi Database và giải phóng tài nguyên trên remote SeaVoice engine (DELETE /v1/tts/voices/{id}).

6. Danh Mục React Query Hooks Phục Vụ 4 Tabs (use-seavoice.ts)

Bảng tổng hợp các Hooks dùng chung trong toàn bộ các Tab:

Hook NameLoạiMục ĐíchCache Invalidation
useAvailableVoices()QueryLấy danh sách giọng khả dụng (Team + Approved Public)seavoiceKeys.availableVoices()
useMyVoices()QueryLấy danh sách giọng do chính team sở hữuseavoiceKeys.myVoices()
useEmotions()QueryLấy danh sách nhãn cảm xúc phi ngôn ngữseavoiceKeys.emotions()
useLanguages()QueryLấy danh sách ngôn ngữ hỗ trợ (vi, en)seavoiceKeys.languages()
useGenerateTts()MutationGửi yêu cầu sinh giọng đọc văn bảnKhông đổi DB giọng
useCloneVoice()MutationĐăng ký & sinh giọng nhân bản từ file mẫuInvalidate seavoiceKeys.all
useDesignVoice()MutationĐăng ký & sinh giọng mới từ câu mô tả instructInvalidate seavoiceKeys.all
useDeleteVoice()MutationXóa một giọng khỏi kho lưu trữInvalidate seavoiceKeys.all
useRequestPublicVoice()MutationGửi yêu cầu duyệt giọng ra thư viện chungInvalidate seavoiceKeys.all
useRevokePublicVoice()MutationHủy chia sẻ giọng công cộngInvalidate seavoiceKeys.all

7. Checklist Kiểm Thử Giao Diện 4 Tabs (QA / Acceptance Criteria)

  • Tab 1 (TTS):
    • Hiển thị chính xác tên giọng được truyền từ Tab 2, 3, 4.
    • Chèn thẻ cảm xúc tại đúng vị trí trỏ chuột và duy trì focus của textarea.
    • Bộ đếm ký tự và tiền VNĐ cập nhật realtime theo từng phím gõ.
    • Phát thanh mượt mà sau khi render xong qua AudioPlayerBar.
  • Tab 2 (Clone):
    • Báo lỗi ngay nếu file tải lên vượt quá 10MB hoặc sai định dạng âm thanh.
    • Hiển thị trình phát nghe thử file mẫu trước khi bấm Submit.
    • Tự động chuyển về Tab 1 và điền sẵn giọng mới khi clone thành công.
  • Tab 3 (Design):
    • Chọn preset tự động cập nhật text instruct, tên và giới tính tương ứng.
    • Chọn thẻ mới cùng danh mục tự động ghi đè thẻ cũ.
    • Hiển thị cảnh báo màu cam nếu có từ khóa ngoài Whitelist.
  • Tab 4 (Registry):
    • Chuyển tab lọc "Tất cả", "Của tôi", "Công đồng" hoạt động chính xác.
    • Tìm kiếm realtime theo tên và VoiceID không phân biệt hoa thường.
    • Quyền xóa và chia sẻ chỉ xuất hiện trên các giọng thuộc quyền sở hữu của Team.