
Dự án giải quyết vấn đề gì
Dự án này không phải là một gói phần mềm dùng ngay sau khi cài đặt, mà là một phương pháp sản xuất giọng nói AI đã được cải tiến qua quá trình nghe thử thực tế. Nó nhắm đến một số vấn đề thường gặp: tiếng thở rơi lẻ loi trong khoảng im lặng, khoảng cách quá lâu giữa hơi thở và lời thoại, giọng nói đột nhiên trở nên trang trọng sau đoạn ngắt, mô hình lặp lại hoặc bỏ sót câu, và tiếng môi trường không thể nối tự nhiên sau khi cắt lời thoại.
Quy trình hoàn chỉnh xoay quanh bốn việc: dùng ElevenLabs v4 để tổng hợp giọng nói, dùng ffmpeg xử lý âm thanh, dùng Whisper chuyển lời nói thành văn bản để đối chiếu, sau đó dùng NumPy phân tích độ lớn, cao độ và vị trí mẫu.
Kết luận cốt lõi không phải là “thêm nhiều tiếng thở hơn”, mà là “đặt hơi thở vào đúng vị trí”: hơi ngắn trước khi mở lời phải sát chữ đầu tiên, hơi ở cuối câu thuận thế nối ra, còn hơi hít vào giữa câu nên được giữ lại giữa hai nửa câu.
Năng lực và phương pháp chính
- Đặt hơi thở sát lời thoại: nhận diện hơi nhẹ và ngắn trước khi mở lời, chỉ di chuyển đoạn im lặng để đưa hơi thở đến vị trí cách lời thoại khoảng 0.08 giây.
- Sửa độ lớn đột ngột: đo độ lớn của từng khối âm thanh có tiếng, rồi giảm vừa phải những khối cao hơn trung vị trên 4 dB.
- Cắt câu không làm hỏng bản gốc: cắt tại vùng im lặng của giọng khô, sau đó dựng lại tiếng môi trường dạng lặp, cố gắng giữ nguyên phần trình diễn đã được chấp nhận.
- Phát hiện lặp và bỏ sót lời: dùng Whisper chuyển bản tổng hợp thành văn bản, so sánh “những gì nghe được” với “những gì đáng lẽ phải đọc”; khi phát hiện bất thường thì tạo lại.
- Đồng bộ tiếng động với hành động: đặt dấu tiếng động vào câu mà hành động thực sự bắt đầu, thay vì thống nhất đặt ở đầu mỗi lượt lời thoại.
- Tạo dựng chất giọng đời thường: không chỉ làm giọng mềm hơn, mà còn xác định rõ tốc độ, cao độ, mức độ chắc chắn và thói quen nói của nhân vật.
Chuẩn bị môi trường
Công cụ cần thiết
- ElevenLabs v4: phụ trách chuyển văn bản thành giọng nói. Dự án thực tế sử dụng v4.
- ffmpeg: dùng để giải mã, cắt, nối, đổi tần số lấy mẫu và xuất âm thanh.
- Whisper: chuyển kết quả tạo ra thành văn bản để phát hiện lặp, bỏ sót câu và sai lệch nội dung.
- NumPy: dùng để xử lý mảng âm thanh, tương quan chéo, tính độ lớn và di chuyển đoạn âm thanh.
Môi trường được kiểm thử trong README là macOS. Dự án không cung cấp script cài đặt cố định, vì vậy nên cài đặt từng công cụ theo hướng dẫn chính thức tương ứng, đồng thời xác nhận ffmpeg, Whisper và môi trường Python đều hoạt động bình thường.
Trước khi bước vào giai đoạn phân tích, cần giải mã âm thanh thành dạng sóng đơn kênh với tần số lấy mẫu thống nhất, đồng thời chuẩn bị hai nhóm dữ liệu được tính theo bước cố định: cao độ mỗi khung f0 và độ lớn RMS mỗi khung rms. Quy trình gốc sử dụng bước khung 10 mili giây; cửa sổ phân tích độ lớn sẽ làm mờ ranh giới vài chục mili giây, vì vậy khi di chuyển đoạn âm thanh cần chừa lại một khoảng nhỏ.
Luôn lưu hai bản âm thanh
Sau mỗi lần tạo, hãy lưu các phiên bản sau:
- Giọng khô: chỉ có giọng người, không bao gồm tiếng môi trường hoặc tiếng động tác.
- Giọng ướt: phiên bản cuối cùng đã phủ tiếng động bên dưới giọng người.
Giọng khô không chỉ thuận tiện cho việc đo hơi thở, mà còn giúp giữ nguyên phần trình diễn ban đầu khi cần xóa một câu, thay một đoạn hoặc dựng lại lớp nền về sau. Không nên chỉ lưu bản hòa âm cuối cùng.
Bước 1: Viết đúng lời nhắc cho hơi thở
Đừng đặt “ừm”, “ha” hoặc nhãn hơi thở thành từng dòng riêng giữa những khoảng im lặng dài. Âm thanh tạo ra theo cách này thường có cao độ rõ rệt, độ lớn gần với lời nói bình thường, đồng thời có những khoảng im lặng chết rất dài ở trước và sau, khiến nó giống một hiệu ứng âm thanh độc lập.
Nên viết hơi thở vào chính dòng chứa lời thoại:
[low, breathy, wet]
[inhales] Nhìn tôi…… [exhales]
[long pause]
Ừm… Đừng trốn…… Ừm… ha…- Ở đầu dòng chỉ dùng nhãn hơi thở ngắn, chẳng hạn
[inhales],[exhales]hoặc[sighs]. - Không đặt thêm một chuỗi dấu chấm lửng giữa nhãn và lời thoại.
- Cuối câu có thể thuận thế kéo theo một hơi thở, hoặc liên tiếp hai đến ba tiếng ngữ khí nhẹ.
- Trong khoảng trống chỉ viết một dòng
[long pause], không xếp các tiếng ngân độc lập trong khoảng trống. - Cũng có thể đặt hơi hít vào giữa câu; mỗi câu chỉ cần chọn một hoặc hai vị trí ở đầu câu, giữa câu hoặc cuối câu.
Viết liên tiếp hai [long pause] chưa chắc làm đoạn ngắt dài lên rõ rệt. Trong kiểm thử thực tế của dự án, một nhãn tạo ra khoảng im lặng khoảng 0.96 giây, còn hai nhãn liên tiếp tạo ra khoảng 1.04 giây. Vì vậy không nên dựa vào mặt chữ của nhãn để kiểm soát thời lượng chính xác.
Bước 2: Chia âm thanh thành các “khối” có thể đo lường
“Khối” ở đây là một đoạn liên tục có âm thanh. Mỗi khối được đo ba chỉ số: thời lượng, độ lớn trung bình và tỷ lệ các khung có cao độ. Những chỉ số này giúp phân biệt lời thoại bình thường, hơi thở thật và tiếng ngân độc lập có cao độ.
def is_breath(f0, rms, a, b):
dur = (b - a) * 0.01
voiced = (f0[a:b] > 0).mean()
db = 20 * np.log10(rms[a:b].mean() + 1e-9)
return 0.12 <= dur <= 0.9 and voiced < 0.15 and db < -29Khoảng điển hình trong dự án gốc như sau:
- Lời thoại hạ thấp, có hơi: khoảng 0.5 đến 2 giây, độ lớn khoảng −22 đến −27 dB, các khung có cao độ chiếm khoảng hai đến bốn phần mười.
- Hít vào, thở ra hoặc tiếng xì: khoảng 0.3 đến 0.65 giây, độ lớn khoảng −32 đến −40 dB, các khung có cao độ chiếm khoảng một phần mười.
- Tiếng ngân độc lập bị tách rời: khoảng 0.4 đến 0.5 giây, độ lớn có thể đạt −25 dB, các khung có cao độ vượt quá sáu phần mười, trước và sau thường có 0.6 đến 1 giây im lặng.
Các ngưỡng này xuất phát từ một chất giọng và bộ mẫu cụ thể, không thể xem trực tiếp là tiêu chuẩn chung cho mọi giọng nói. Cách đáng tin cậy hơn là trước hết để người nghe chỉ ra vấn đề, sau đó dùng các đoạn liền kề bình thường để thiết lập phạm vi riêng cho chất giọng đó.
Bước 3: Đưa hơi thở trước khi mở lời đến sát lời thoại
Ngay cả khi lời nhắc đã được viết đúng, mô hình vẫn có thể tự để lại khoảng im lặng 0.3 giây hoặc lâu hơn giữa hơi thở và lời thoại. Lời nhắc khó kiểm soát vị trí ở cấp vài chục mili giây, vì vậy cần di chuyển đoạn âm thanh sau khi tạo.
Cấu trúc trước và sau xử lý có thể hiểu như sau:
Trước xử lý: câu trước → im lặng A → hơi thở → im lặng B → câu sau
Sau xử lý: câu trước → im lặng dài hơn → hơi thở → 0.08 giây → câu sauKhi thao tác, chỉ hoán đổi vị trí của hơi thở và khoảng im lặng, không thay đổi bản thân hơi thở hay lời thoại, cũng không thay đổi tổng thời lượng của cả đoạn. Nhờ vậy, timeline, lớp tiếng động và thống kê tốc độ nói hiện có đều không bị phá vỡ.
Đoạn ứng viên phù hợp để di chuyển cần đáp ứng:
- Được xác định là hơi thở nhẹ, ngắn và không có cao độ rõ rệt.
- Trong khoảng khoảng 0.14 đến 1.5 giây phía sau nó xuất hiện một khối lời nói.
- Phía trước đã có ít nhất khoảng 0.3 giây trống.
- Không di chuyển hơi hít vào ngay sau nửa câu đầu, để tránh phá hỏng nhịp lấy hơi tự nhiên vốn có giữa câu.
Mục tiêu kiểm thử thực tế trong README là khoảng cách khoảng 0.08 giây. Khi di chuyển, có thể lấy thêm khoảng 0.02 giây ở đầu hơi thở và khoảng 0.03 giây ở cuối, đồng thời tạo fade khoảng 4 mili giây ở đầu và cuối tại vị trí mới để giảm cảm giác nối ghép.
Bước 4: Sửa “giọng nghiêm túc” sau đoạn ngắt
Một số giọng nói sau đoạn ngắt dài sẽ trở lại trạng thái nói thông thường rõ hơn, to hơn và ít hơi hơn. Trong trường hợp mẫu, câu ngắn bất thường to hơn nội dung xung quanh trên 6 dB, còn tỷ lệ khung có cao độ tăng từ khoảng ba phần mười lên sáu phần mười.
Trước hết giảm khả năng xảy ra bằng lời nhắc
- Sau đoạn ngắt hãy gắn một hơi thở, hoặc để một tiếng ngân trầm nhẹ trượt vào lời thoại.
- Cuối câu dùng cách kết thúc kéo dài, không phải lần nào cũng dùng dấu hỏi hoặc dấu chấm dứt khoát.
- Câu ngắn gồm hai hoặc ba từ có thể gộp với câu liền kề, tránh bị đọc riêng thành giọng phát thanh rõ ràng.
- Khi cần nhấn lại chất giọng, chèn nhãn nhỏ vào đầu lời thoại, không phải lúc nào cũng chiếm riêng một dòng.
[long pause]
Ừm… [low, breathy] Đừng trốn……Nếu bộ chia đoạn tạo một đoạn mới tại mỗi nhãn chất giọng ở đầu dòng, cách viết trong dòng này còn giúp giảm mối nối và bước nhảy cao độ.
Sau đó dùng chương trình để hạ độ lớn bất thường
Tính độ lớn theo từng khối, lấy trung vị của các khối lời nói trong đoạn hiện tại làm tham chiếu. Nếu một khối cao hơn trên 4 dB, hãy hạ nó xuống chỉ còn cao hơn khoảng 2 dB, suy giảm tối đa 6 dB, đồng thời dùng bao biên chuyển tiếp khoảng 25 mili giây ở đầu và cuối.
Bắt buộc đo theo từng khối, không đo theo từng câu. Một âm thanh kéo dài nửa giây đột ngột nổi lên, nếu gộp với câu dài phía sau để lấy trung bình, bất thường 6 dB có thể bị pha loãng xuống dưới ngưỡng. Chương trình chỉ có thể sửa âm lượng, không thể trực tiếp khôi phục màu âm bị thiếu hơi, vì vậy cần dùng đồng thời lời nhắc và xử lý hậu kỳ.
Bước 5: Kiểm tra chuyển lời thành văn bản ngay sau khi tổng hợp
Đôi khi mô hình đọc một câu hai hoặc ba lần, hoặc bỏ hẳn câu cuối. Bản thảo có thể hoàn toàn chính xác, vì vậy mỗi đoạn tổng hợp đều nên được chuyển lời thành văn bản một lần để đối chiếu.
Dự án sử dụng hai loại tín hiệu:
- Sau khi bỏ nhãn trong ngoặc vuông và các từ ngữ khí thường gặp, tìm những đoạn dài ít nhất sáu chữ Hán xuất hiện lặp lại trong bản chuyển lời nhưng chỉ xuất hiện một lần trong bản gốc.
- So sánh số chữ trong bản chuyển lời với số chữ trong bản gốc; nếu ít hơn dự kiến khoảng hai phần mười thì xem là có khả năng nuốt mất câu.
Khi phát hiện bất thường, tạo lại một lần, sau đó chấm điểm hai phiên bản và giữ phiên bản có điểm thấp hơn. Các từ ngữ khí thường không được chuyển lời chính xác, vì vậy không nên dùng “ừm, ha, à, ờ” làm căn cứ đối chiếu chính.
Trong cuộc gọi thời gian thực, Whisper có thể cần khoảng ba giây để chuyển lời 20 đến 30 giây âm thanh. Có thể kiểm tra các đoạn tiếp theo ở chế độ nền trong khi đoạn hiện tại đang phát; đoạn đầu tiên không có thời gian phát để che độ trễ, nên có thể sàng lọc nhanh bằng “thời lượng chia cho số chữ hiệu dụng”. Phạm vi bình thường trong trường hợp gốc khoảng 0.41 đến 0.62 giây mỗi chữ; khi vượt phạm vi mới khởi động chuyển lời đầy đủ.
Bước 6: Xóa một câu mà không cần tạo lại
Nếu người nghe thích một phần trình diễn cụ thể của một lần tạo nào đó, đừng đọc lại cả đoạn chỉ vì cần xóa một câu. Mỗi lần tạo là một buổi diễn mới; hơi thở, đoạn ngắt và chi tiết cuối câu rất khó tái hiện chính xác.
- Xác định vùng im lặng: trước tiên vẽ độ lớn theo đơn vị khoảng 50 mili giây, sau đó chuyển từng đoạn ứng viên nhỏ thành văn bản để xác nhận đó là lời thoại, hơi thở hay khoảng trống.
- Cắt từ giọng khô: để hai đầu mối nối đều rơi vào vị trí yên tĩnh nhất, đồng thời giữ fade-in và fade-out vài chục mili giây ở đầu và cuối mỗi mảnh.
- Dựng lại tiếng động: không cắt trực tiếp bản thành phẩm đã phủ lớp nền, nếu không nhịp của tiếng động lặp có thể nhảy tại mối nối.
- Chuyển lời lại thành văn bản: xác nhận thành phẩm chỉ còn các câu mục tiêu.
- Đối chiếu theo từng mẫu: kiểm tra các vùng không chỉnh sửa vẫn nhất quán với âm thanh gốc.
Nếu không biết giọng ướt đã dùng đoạn vật liệu lặp nào, có thể trích xuất dạng sóng thành phẩm trong khoảng giọng người hoàn toàn im lặng, sau đó cho các vật liệu ứng viên lặp lại và thực hiện tương quan chéo. Vị trí có tương quan cao nhất có thể cho biết pha của vật liệu, còn tích vô hướng có thể ước tính mức tăng của lớp nền. Trong trường hợp mẫu, độ tương quan giữa hai vật liệu đạt 0.99, mức tăng ước tính lần lượt khoảng 0.200 và 0.241.
Việc đối chiếu phải chính xác đến từng mẫu. Chỉ cần lệch hơn mười mẫu cũng có thể khiến độ tương đồng giảm rõ rệt, dẫn đến phán đoán sai rằng “đã đặt nhầm vật liệu nền”. Sau khi căn chỉnh, độ tương đồng của các đoạn không chỉnh sửa trong trường hợp mẫu đạt 0.996 đến 0.998.
Bước 7: Để tiếng động bắt đầu đúng thời điểm
Dự án quyết định loại tiếng động nền bằng các dấu tùy chỉnh trong lời nhắc, sau đó xóa các dấu này trước khi gửi nội dung đến ElevenLabs. Quy tắc then chốt là: âm thanh bắt đầu từ câu nào thì dấu phải được viết ở câu đó.
licking: tương ứng với tiếng ướt chậm.fingering: tương ứng với tiếng động tác dính hơn; trạng thái nhanh có thể viếtfast fingering.sucking: tương ứng với tiếng ướt chậm.thrust: chỉ xuất hiện trước câu mà hành động thực sự bắt đầu, không được đặt sớm ở đầu cả lượt thoại.still: dùng để dừng tiếng động lặp hiện tại.
Nếu dấu kích hoạt việc chia đoạn, vị trí hành động thực sự bắt đầu đáng được giữ lại như một mối nối mới; các nhãn nhỏ khác chỉ dùng để nhắc lại chất giọng thì nên đặt bên trong phần mở đầu lời thoại.
Bước 8: Thiết kế chất giọng nói chuyện bình thường
“Không tự nhiên” không nhất thiết có nghĩa là giọng chưa đủ dịu dàng. Dự án gốc trước tiên nâng cấp mô hình cũ lên ElevenLabs v4 và thử các lời nhắc thư giãn, mềm mại hơn, nhưng người nghe cho rằng nhân vật vì thế đã trở thành một người khác. Hướng cuối cùng có hiệu quả là xác định rõ khẩu khí riêng của nhân vật.
Lời nhắc cơ bản được chọn là:
[low, slow, confident, a little teasing]Thiết lập tương ứng là v4, độ ổn định 0.35. Ở đầu câu có thể ngẫu nhiên thêm [exhales], [chuckles softly], một tiếng “ừm……” hoặc không thêm gì cả. Không nên lần nào cũng thêm, nếu không sự biến đổi tự nhiên sẽ biến thành câu cửa miệng cố định.
- Chỉ sửa văn bản gửi đi để tổng hợp, không sửa nguyên văn trong giao diện trò chuyện và lịch sử.
- Nếu câu vốn đã bắt đầu bằng từ ngữ khí, không thêm chồng lần nữa.
- Các nội dung đã có nhãn chất giọng hoặc hoàn toàn bằng tiếng Anh có thể bỏ qua bước tự động bao bọc.
- Điện thoại sử dụng hệ thống lời nhắc riêng, không áp dụng phần mở đầu ngẫu nhiên của giọng nói đời thường.
Kỹ thuật nâng cao và các bẫy thường gặp
Quy tắc lời nhắc nên viết “cần làm gì”
Một câu “không được viết loại âm thanh nào đó” có thể khiến bộ tạo văn bản ngừng sử dụng nó hoàn toàn. Cách đáng tin cậy hơn là viết rõ loại âm thanh cần xuất hiện, vị trí và tần suất tối thiểu, sau đó giao cho chương trình phát hiện các cao độ bất thường hoặc cách đọc khác lạ thỉnh thoảng xuất hiện.
Không tái sử dụng cùng một hơi thở
Khi chuyển một đoạn gốc ưng ý sang phiên bản mới, có thể lấy vật liệu từ cùng một lần tạo, nhưng không nên lặp lại cùng một hơi thở ở hai vị trí. Nếu không, người nghe có thể nhận ra chính xác cùng một chi tiết đã bị sao chép.
Xây dựng kho vật liệu có thể tìm kiếm
Lưu các âm hít vào, tiếng rít và nhịp thở ngắn đã được chấp thuận theo tệp gốc, khoảng thời gian và vị trí sử dụng. Nếu sau này một nhãn thở nào đó chỉ tạo ra sự im lặng, bạn có thể bổ sung một nhịp thở từ kho tư liệu. README cũng chỉ ra rằng việc tự động bổ sung hơi thở vẫn cần giao diện có dấu thời gian; chỉ dựa vào “sự im lặng xuất hiện giữa câu” thì không thể phân biệt hơi thở bị thiếu với khoảng dừng tự nhiên ở dấu chấm lửng.
Điện thoại thời gian thực cần xử lý việc chen lời
Khi hệ thống chưa phát câu trả lời mà đối phương đã bắt đầu nói, hãy loại bỏ câu trả lời cũ chưa phát ra và xử lý trực tiếp nội dung mới. Đừng lưu câu nói mới vào bộ đệm cho đến khi câu trả lời cũ phát xong, nếu không cùng một câu có thể bị phản hồi hai lần. Với những âm thanh ngắn không thể nhận dạng thành văn bản, quy trình ban đầu đã kéo dài thời gian chờ để tự động chèn âm thanh ngữ điệu từ 2.5 giây lên 4.5 giây, nhằm giảm khả năng kích hoạt trước lời nói thực.
Nghe ra vấn đề trước, rồi mới quyết định đo gì
Độ lớn, cao độ, độ dài khoảng lặng, tỷ lệ số từ và độ tương quan chéo chỉ có thể xác minh những vấn đề đã được phát hiện. Chúng không thể tự mình phán đoán “sắc thái có đúng hay không”. Quy trình hiệu quả nhất vẫn là để người nghe chỉ ra vị trí cụ thể nghe không tự nhiên trước, rồi xây dựng chỉ số tương ứng.
Quy trình hoàn chỉnh được đề xuất
- Soạn văn bản có đánh dấu đường giọng, hơi thở, khoảng dừng và âm thanh hành động.
- Xóa các dấu âm thanh hành động chỉ dùng cho hậu kỳ, rồi đưa phần văn bản còn lại vào ElevenLabs v4.
- Lưu bản thu giọng khô và dùng Whisper để chép lời, kiểm tra nội dung lặp, câu bị bỏ sót và sai lệch nội dung.
- Tính
f0vàrms, chia âm thanh thành các khối lời nói, khối hơi thở và các khối có âm khác. - Di chuyển hơi thở trước khi bắt đầu nói đến vị trí cách lời thoại khoảng 0.08 giây, đồng thời giữ lại hơi thở giữa câu.
- Phát hiện độ lớn đột ngột ở từng khối và làm suy giảm mượt các khối bất thường.
- Dựa trên các dấu hành động để bố trí âm thanh hành động lặp, rồi xuất bản có âm thanh hòa trộn.
- Chép lời lần nữa, kiểm tra tổng thời lượng, số khoảng lặng dài, vị trí hơi thở và độ tương đồng ở cấp mẫu của các vùng chưa chỉnh sửa.
- Lưu các đoạn gốc đã được người nghe chấp thuận cùng khoảng thời gian của chúng, không chỉ giữ lại bản hòa âm cuối cùng.
Kết luận
Giọng nói AI tự nhiên không có được nhờ chồng thêm thật nhiều nhãn hơi thở. Cách đáng tin cậy hơn là để hơi thở bám sát lời nói, để âm thanh hành động bắt đầu đúng thời điểm, để mọi kết quả tổng hợp đều được đối chiếu bằng bản chép lời, và giữ lại phần trình diễn ban đầu nhiều nhất có thể khi cần chỉnh sửa.
Lời nhắc quyết định “viết gì” và “nói với giọng thế nào”, còn chương trình xử lý vị trí ở mức vài chục mili giây, độ lớn bất thường, việc lặp hoặc bỏ sót lời và sự nối tiếp ở cấp mẫu. Chỉ khi phối hợp cả hai, một đoạn giọng hay được tạo ra ngẫu nhiên mới có thể trở thành quy trình sản xuất có thể lặp lại. Có thể tham khảo thêm thông tin nền trong hướng dẫn viết lời nhắc của ElevenLabs, tài liệu về âm thanh hơi thở của ElevenLabs và nghiên cứu về hơi thở khi nói.
