CHƯƠNG 3: XÂY DỰNG HỆ THỐNG TTS TIẾNG VIỆT VÀ ỨNG DỤNG ĐỌC BÁO ĐIỆN TỬ
3.2. Phân tích và thực hiện giải thuật TTS
3.2.2. Khối Phân đoạn và Lựa chọn đơn vị
Nội dung văn bản sau khi chuẩn hóa được đưa vào bộ lựa chọn đơn vị. Đầu tiên văn bản được tách thành từng câu dựa vào dấu câu, từng câu sẽ được phân đoạn thành các đơn vị bằng cách tra từ điển phân đoạn đơn vị. Sau bước này, văn bản được chuyển thành các đơn vị cơ sở tương ứng trong từ điển phân đoạn. Tiếp theo là phần lựa chọn đơn vị âm thanh cho các đơn vị văn bản vừa được phân đoạn. Thông tin của mỗi đơn vị cơ sở được lấy ở từ điển thông tin đơn vị, đơn vị cơ sở cần lựa chọn được so sánh với đơn vị bên trái và đơn vị bên phải để có thể chọn các đơn vị liên tiếp trong cùng một câu thu âm. Việc lựa chọn các đơn vị liên tiếp trong cùng một câu thu âm tạo nên sự liên tục, giảm các điểm ghép nối cho câu nói tổng hợp.
Tra từ điển phân đoạn đơn vị
Bắt đầu
Lấy nội dung file
Không tìm thấy từ?
Lưu chỉ số tìm được vào mảng ID_arr[]
Hết văn bản?
Lưu thông tin đơn vị
Kết thúc
Tìm kiếm trong từ điển thông tin đơn vị
Đánh vần từng chữ cái của từ
Đ
S
Đ
S
Hình 3-3 Lưu đồ giải thuật tra từ và lựa chọn đơn vị 3.2.2.2. Sử dụng hàm băm trong tìm kiếm
Hàm băm (tiếng Anh: Hash Function) là giải thuật nhằm sinh ra các giá trị băm
hướng đối tượng ...). Giá trị băm đóng vai gần như một khóa để phân biệt các khối dữ liệu, tuy nhiên, người ta chấp hiện tượng trùng khóa hay còn gọi là đụng độ và cố gắng cải thiện giải thuật để giảm thiểu sự đụng độ đó. Hàm băm thường được dùng trong bảng băm nhằm giảm chi phí tính toán khi tìm một khối dữ liệu trong một tập hợp (nhờ việc so sánh các giá trị băm nhanh hơn việc so sánh những khối dữ liệu có kích thước lớn).
Vì tính thông dụng của bảng băm, ngày nay, đa số ngôn ngữ lập trình đều cung cấp thư viện ứng dụng bảng băm, thường gọi là thư viện trong đó có các vấn đề như:
tập hợp (collection), danh sách (list), bảng (table), ánh xạ (mapping), từ điển (dictionary). Thông thường, các lập trình viên chỉ cần viết hàm băm cho các đối tượng nhằm tích hợp với thư viện bảng băm đã được xây dựng sẵn.
Một hàm băm tốt phải thỏa mãn các điều kiện sau:
- Tính toán nhanh
- Các khoá được phân bố đều trong bảng - Ít xảy ra đụng độ
- Xử lý được các loại khóa có kiểu dữ liệu khác nhau
Hình 3-4 Mô tả hoạt động của hàm Hash và bảng Hash
Trong khối lựa chọn đơn vị này sử dụng các macros của thư viện uthash.h nhằm tăng tốc độ đơn giản hóa việc tìm kiếm. File header uthash.h là tập hợp các macros
hóa việc tra cứu từ điển. Trong uthash.h, bảng băm sẽ bao gồm các struct. Mỗi struct này đại diện cho sự kết hợp của một sự kết hợp giữa từ khóa và giá trị quy ước cho từ khóa đó. Một trong các phần tử của struct sẽ đóng vai trò là từ khóa, địa chỉ tới struct sẽ đóng vai trò là giá trị.
Định nghĩa một cấu trúc có thể sử dụng bảng băm trong uthash.h:
#include "uthash.h"
struct my_struct {
int id; /* key */
char name[10];
UT_hash_handle hh; /* makes this structure hashable */
};
Trong đó UT_hash_handle bắt buộc phải có trong struct để có thể sử dụng hàm băm. Đối với hệ thống 32 bits, mỗi phần từ UT_hash_handle sẽ chiếm dung lượng là 32 bytes.
Để khởi tạo, bảng băm cần phải được gán giá trị NULL lúc đầu
struct my_struct *users = NULL; /*Important! Initialize to NULL */
Để thêm một phần tử vào bảng băm, hàm sau được sử dụng. Khi thêm một phần tử vào bảng băm phải đảm bảo từ khóa thêm không trùng nhau.
void add_user(int user_id, char *name) { struct my_struct *s;
s = malloc(sizeof(struct my_struct));
s->id = user_id;
strcpy(s->name, name);
HASH_ADD_INT( users, id, s ); /* id: name of key field */
}
Trong đó:
- users là bảng băm
- id là tên của phần tử là từ khóa trong struct my_struct - s là pointer tới struct được thêm vào bảng băm
Để tìm kiếm một phần tử sử dụng chìa khóa của nó trong bảng băm, chúng ta dùng hàm sau.
struct my_struct *find_user(int user_id) { struct my_struct *s;
HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */
return s;}
Trong đó:
- user là bảng băm
- users_id là pointer chỉ đến chìa khóa
- s là giá trị xuất ra của macro HASH_FIND_INT. Kết quả cuối cùng của s sẽ chỉ đến cấu trúc có chìa khóa tương ứng, nếu không sẽ trả
về giá trị là NULL
3.2.2.3. Tìm kiếm từ điển và Phân đoạn
Cấu trúc bộ từ điển gồm các từ hoặc cụm từ đã được đánh số sẵn. Khi sử dụng với bảng băm, các từ hoặc cụm từ sẽ đóng vai trò là từ khóa nhằm tìm kiếm các chỉ số quy ước trước của từng cụm từ
…
CÁC TIỂU VƯƠNG QUỐC 13326 CÁC TỈNH LÂN CẬN 13327 CÁC TỈNH NAM BỘ 13328
CÁC TỈNH THÀNH PHỐ 13330
…
Cấu trúc struct cho một từ hoặc cụm từ được định nghĩa như sau, trong đó: char str[80] là chuỗi chứa từ hoặc cụm từ, id là chỉ số của nó trong từ điển.
typedef struct { char str[80];
uint16_t id;
UT_hash_handle hh;
} dict_t;
Việc tìm kiếm thành các đơn vị cơ sở trong từ điển lớn được hỗ trợ bởi hàm Hash nên sẽ đảm bảo về yêu cầu thời gian tìm kiếm. Một văn bản được phân đoạn thành tập các đơn vị cơ sở dựa trên từ điển phân đoạn đơn vị. Phân đoạn làm sao cho ít các thành phần đơn vị cơ sở nhất thì sẽ càng ít các điểm ghép nối, đồng nghĩa âm thanh sau khi tổng hợp ít bị đứt quãng. Do đó, vấn đề phân đoạn một đoạn văn bản thành các đơn vị cơ sở dựa trên từ điển phân đoạn đơn vị là quan trọng. Để làm được như vậy, hàm tìm kiếm và phân đoạn phải tìm ra được đơn vị cơ sở dài nhất (cụm từ có nhiều từ nhất) tùy thuộc vào văn bản ở ngõ vào. Trong trường hợp xấu nhất, trong văn bản có chứa các từ tiếng nước ngoài hoặc không phải tiếng Việt không được chuẩn hóa, thì hệ thống tự động phân thành từng chữ cái. Ví dụ của một trường hợp phân đoạn một đoạn văn bản sau: “THÀNH TÍCH XUẤT SẮC NHẤT”
Thì tốt nhất phải được chia thành hai đoạn:
“THÀNH TÍCH XUẤT SẮC” + “NHẤT”
Các trường hợp phân đoạn còn lại sau đây không tốt bằng nên sẽ không được phân đoạn như vậy trong ứng dụng:
“THÀNH TÍCH” + “XUẤT SẮC” + “NHẤT”
“THÀNH TÍCH” + “XUẤT” + “SẮC” + “NHẤT”
“THÀNH” + “TÍCH” + “XUẤT SẮC” + “NHẤT”
“THÀNH” + “TÍCH” + “XUẤT” + “SẮC” + “NHẤT”
3.2.2.4. Lựa chọn đơn vị
Trong một số trường hợp, cùng một từ khóa nhưng có thể có nhiều từ hoặc cụm từ khác nhau trước và sau đó. Từ điển thông tin đơn vị chứa tập hợp các trường hợp như vậy. Nếu từ điển thông tin đơn vị chứa càng nhiều tập các trường hợp thì chất lượng âm thanh sau khi tổng hợp sẽ càng tốt, nhưng như vậy sẽ làm tăng dung lượng của cơ sở dữ liệu và các file từ điển.
Cấu trúc bộ từ điển lựa chọn đơn vị được minh họa như sau:
- Cột thứ nhất là chỉ số của cụm từ trong file âm thanh - Cột thứ hai là chỉ số cụm từ bên trái nó trong file âm thanh - Cột thứ ba là chỉ số cụm từ bên phải nó trong file âm thanh - Cột thứ tư là tên file âm thanh chứa cụm từ
- Cột thứ năm là vị trí frame bắt đầu của cụm từ trong file âm thanh (một frame = 32 bytes)
- Cột cuối cùng là vị trí frame kết thúc của cụm từ trong file âm thanh 1 257 1060 B180208_016_F_V1 7270 7390
1 257 19203 A180109_086 2320 2410
…
118 0 0 B220209_022 1960 2130
118 3597 19878 B220209_022 1960 2130 118 5638 21363 A200309_065 8900 9050
…
Cấu trúc struct cho một đơn vị âm thanh được mô tả như sau:
typedef struct { int id_mid;
int id_left;
int id_right;
} unit_id_t;
typedef struct { unit_id_t key;
char file_name[24];
uint16_t start;
uint16_t end;
UT_hash_handle hh;
} unit_t;
Trong đó:
- key trong struct unit_t đóng vai trò là chìa khóa trong hàm băm - file_name chứa tên file âm thanh chứa cụm từ
- start là vị trí frame bắt đầu của cụm từ trong file âm thanh - end là vị trí frame kết thúc của cụm từ trong file âm thanh
Sau khi các cụm từ và từ được tra hết, một file danh sách các file âm thanh, vị trí byte bắt đầu và kết thúc sẽ được lưu lại cho quá trình xử lý của Khối Ghép nối.