TỔ CHỨCTRUYXUẤTVÀPHÁTFILETIẾNGNÓI Sau khi đã tìm kiếm dữ liệu trong cơ sở dữ liệu , công việc cuối cùng là thông báokếtquả bằng giọng nóiquađiện thoại cho người gọi nghe, tức là chuyển dữ liệu từ dạng chữ, số sang tiếng nói. Công việc này chủ yếu gồm 2 giai đoạn chính : tìm dữ liệu tiếngnói tương ứng vàphátquađiện thoại . Trước hết, em sẽ giới thiệu về cách tổ chứcvà lưu trữ tiếng nói. I Chọn phương pháp tổng hợp tiếngnói : Ở phần cơ sở lý thuyết tổng hợp tiếng nói, em đã nêu bốn phương pháp ghép âm, trong đó đáng chú ý là phương pháp ghép âm loại một và phương pháp ghép từng từ đơn. Để đảm bảo chất lương âm cho ứng dụng, em dùng phương pháp ghép từ đơn. II Chọn định dạng (format) cho filetiếngnói : Có rất nhiều chuẩn khác nhau cho file âm thanh. Tuy nhiên nếu chọn chuẩn cho ra tiếngnói chất lượng càng cao thì kích thước dữ liệu thu âm cũng sẽ càng lớn. Ở đây, tiếngnói sẽ được truyền quađiện thoại nên không cần chất lượng cao như các bài nhạc, nhưng cũng không quá thấp khiến người gọi khó nghe. Sau khi thử nghiệm, em chọn chuẩn với các thông số như sau : Kiểu mã hóa PCM Số kênh 1 (mono) Tần số lấy mẫu 11025 Hz Số byte trên 1 giây 11025 Số byte trên 1 mẫu 1 Số bit trên một mẫu 8 IIICách tổ chứcfiletiếngnói : Tiếng Việt có khoảng hơn 6000 từ đơn thông dụng. Dù tổ chức như thế nào thì ta cũng phải thu âm cho từng từ. Ngoài ra còn có một số câu nói sẽ được thu nguyên văn như : lời chào, lời tạm biệt, lời yêu cầu, thôngbáo lỗi , vv . Có hai cách tổ chứcfiletiếngnói : 1. Tổ chức thành từng file .wav : Ta sẽ tiến hành thu âm mỗi từ thành một file .wav . Khi cần từ nào thì phátfile .wav tương ứng. Đây là cách bình thường nhất, đơn giản nhất và dễ thực hiện nhất. Với một file .wav đúng chuẩn thì ta có thể dùng bất kỳ chương trình thu nhạc nào để thu, và lúc phát thì cũng có rất nhiều điều khiển hoặc hàm để phátfile .wav này. Vì thế ta sẽ đỡ mất thời gian và công sức để lập trình lại. Tuy nhiên, với số lượng hơn 6000 từ thì đây không phải là cách giải quyết tốt. Lý do là vì sau khi thu âm xong tất cả, ta sẽ có tổng cộng 6000 file .wav được lưu trên đĩa cứng, chưa kể một số file .wav để thu các câu nói nguyên văn. Với số lượng lớn các file .wav như vậy, công việc sao chép hoặc di chuyển sẽ gặp nhiều khó khăn và mất thời gian, Ngoài ra dung lượng đĩa để lưu trữ sẽ bị lãng phí rất nhiều vì cơ chế cấp phát dung lượng đĩa theo cluster. Hơn hết, đối với chương trình này thì cách tổ chức này có một nhược điểm rất lớn, đó là để đọc ra một câu nói, chương trình sẽ phải mở và đóng liên tục rất nhiều file .wav. Điều này làm cho tiếngnói giữa các từ bị gián đoạn, không liên tục một cách tự nhiên, đồng thời tốc độ thực hiện cũng chậm đi rất nhiều. 2. Tổ chức thành các file dữ liệu tiếngnói (chỉ lưu trữ các mẫu âm thanh) : Như đã giới thiệu trong phần cơ sở lý thuyết, mỗi file âm thanh sẽ gồm 2 phần : phần header và phần dữ liệu (data) . Mặc dù ta phải thu âm từng từ nhưng các lần thu này đều phải theo một chuẩn nhất định, hay nói cách khác là các thông số trong phần header phải hoàn toàn giống nhau. Dựa vào đặc điểm này, ta có thể cắt bỏ các header ra, chỉ lưu trữ các mẫu âm thanh. Như vậy ta có thể chỉ cần 1 file duy nhất để lưu trữ các mẫu âm thanh này. Tuy nhiên, kích thước file này sẽ lên tới hàng chục MB. Điều này sẽ làm chậm quá trình đọc file để phát cũng như sẽ làm chậm quá trình cập nhật hoặc xóa dữ liệu trong file này (tức là khi thu hoặc xóa 1 từ). Để giải quyết vấn đề này, em chia thành 24 file (*.sam) tương ứng với 24 chữ cái tiếng Anh. Mỗi file này sẽ chứa dữ liệu của các từ thuộc cùng 1 chữ cái đầu tiên (ví dụ : “trinh”, “thành” thuộc file T.sam). Với tiếng Việt thì có thêm 1 số chữ cái riêng như : Đ , Ă , Â , Ê, Ô , Ổ, Ở , Ú, vv Đây thực ra chỉ là các chữ cái cơ bản có ghép thêm dấu vào. Vì vậy, em sắp chúng theo chữ cái cơ bản, tức là “Đ” sẽ thuộc file D.sam , “Ă” sẽ thuộc file A.sam, “Ú” sẽ thuộc file U.sam Song song đó, ta phải tổ chức một bảng chỉ mục (index) cho các từ đã được thu âm. Bảng chỉ mục này sẽ gồm có 4 mục : từ được thu âm, vị trí bắt đầu (FileOffset) , kích thước của phần dữ liệu tiếngnói (DataSize) và tên nhóm (chữ cái cơ bản đầu tiên) ứng với từ này trong file *.sam . Thông thường, bảng chỉ mục này sẽ được tổ chức thành một file riêng lẻ. Khi cần sẽ mở file này, dùng một thuật toán để tìm kiếm từ đã được thu âm, sau đó đọc ra giá trị FileOffset , DataSize và tên nhóm tương ứng. Dựa vào 3 giá trị này, ta sẽ mở file *.sam tương ứng để đọc phần dữ liệu tiếngnói vào bộ nhớ , sau đó phát đi cùng với một header được quy định trước. Tuy nhiên, ở chương trình này, em sẽ ghép bảng chỉ mục này vào trong file cơ sở dữ liệu KQHT.mdb . Khi cần ta chỉ việc dùng phương thức Seek của đối tượng Recordset để tìm từ được thu âm và tiếp tục tương tự như trên. Với cách tổ chức dữ liệu tiếngnói như thế này, ta đã khắc phục được các nhược điểm mà cách trên đã mắc phải. Tuy nhiên, mọi công việc từ thu âm, ghi vào file cho đến đọc filevàphát ra âm thanh thì ta phải tự làm lấy, có nghĩa là ta phải viết rất nhiều mã lệnh để thực hiện. Do đó sẽ mất khá nhiều thời gian và nếu không nghiên cứu kỹ , chương trình sẽ chiếm nhiều tài nguyên của hệ thốngvà có thể xảy ra xung đột, tranh chấp tài nguyên hệ thống với các ứng dụng khác. Mặc dù vậy, sau khi chạy thử, chương trình hoạt động khá tốt và nhanh hơn. Đây là cách tổ chức dữ liệu tiếngnói được sử dụng trong chương trình này. Ngoài ra, còn có thêm một file @.sam chứa dữ liệu của các câu thông báo, lời chảo . Sở dĩ em chọn tên “@” mà không phải một tên nào khác là vì trong bảng mã ASCII , ký tự “@” đứng trước ký tự “A”. Nhờ đó ta có thể đưa vào vòng lặp từ ký tự “@” tới ký tự “Z” cho một số việc nào đó (ví dụ mở tất cả các file *.sam). Khi người dùng đang thu lại một số từ nào đó, các file *.sam sẽ bị thay đổi. Vì vậy, những lúc này, khi có người gọi tới, hệ thống phải thôngbáo cho người gọi biết hệ thống đang bận cập nhật dữ liệu. Dữ liệu của câu thôngbáo này phải để ra một file riêng (wait.sam). FileOffset và DataSize của khối dữ liệu này cũng được lưu trong bảng chỉ mục như những khối dữ liệu khác trong các file *.sam . I. Chọn phương pháp phátvà thu tiếngnói : Vì các dữ liệu tiếngnói được ta tự tổ chứcvà lưu trữ trong các file *.sam nên để phátvà thu thì ta chỉ có thể sử dụng các hàm multimedia cấp thấp của Windows. Ta sẽ điều khiển toàn bộ quá trình này, từ việc cấp phát bộ nhớ , định vị khối dữ liệu tiếngnói trong file *.sam cho đến việc chọn thiết bị âm thanh vàpháttiếngnói đi, hoặc thu tiếngnói vào vùng đệm và lưu vào file *.sam. II. Đọc dữ liệu tiếngnói vào bộ nhớ : Để phát một câu nói, ta phải tách câu đó thành các từ đơn lẻ, sau đó tìm dữ liệu tiếngnói tương ứng của các từ đó rồi phát đi. Tuy nhiên, mỗi lần như thế hệ thống sẽ phải thực hiện hàng loạt các công việc như khởi tạo và dọn dẹp vùng đệm , mở và đóng thiết bị vào ra âm thanh. Vì vậy, ở đây em sẽ không phát lần lượt từng từ mà sẽ đọc hết vào bộ nhớ rồi phát đi một lần. Nhờ đó tốc độ và chất lượng âm thanh được cải thiện đáng kể. Các bước thực hiện như sau : - Mở các file *.sam bằng hàm mmioOpen() - Tách một chuỗi thành các từ đơn - Kiểm tra nếu từ nào là số thì chuyển thành chữ - Tìm lần lượt các từ đó trong bảng chỉ mục đồng thời lấy ra 3 giá trị FileOffset , DataSize và tên nhóm , nếu từ nào không có thì sẽ bỏ qua. - Lần lượt đọc vào vùng đệm dữ liệu tiếngnóicủa từng từ . Vùng đệm này được tạo ra có kích thước bằng tổng kích thước của các dữ liệu tiếng nói. Để tìm đến đúng offset trong file *.sam và đọc dữ liệu ra, ta sử dụng 2 hàm thuộc bộ hàm multimedia : mmioSeek() và mmioRead() III. Phát dữ liệu tiếngnói từ vùng đệm : Muốn phát âm thanh đi, ta phải chỉ ra thiết bị mà âm thanh sẽ xuất ra. Ở đây, thiết bị xuất âm thanh chính là modem. Vậy trước hết, ta phải lấy giá trị ID tương ứng cho cuộc gọi được kếtnốithôngqua modem. Cuộc gọi này có handle được lưu giữ ngay khi có tín hiệu gọi đến, kết hợp với handle của line hiện tại để truyền cho hàm lineGetID(). Giá trị ID này sẽ thay đổi tương ứng với mỗi cuộc gọi đến. Vì vậy, ta chỉ cần thực hiện việc này một lần cho mỗi cuộc gọi đến. Sau khi có ID của thiết bị xuất âm thanh và vùng đệm dữ liệu tiếngnói đã sẵn sàng, ta lần lượt gọi các hàm waveOutOpen(), waveOutPrepareHeader(), waveOutWrite() để bắt đầu phát đi cùng với một vài thông số của header đã nêu ở phần 2. Tất cả các hàm này đều đã được giới thiệu trong phần cơ sở lý thuyết. IV. Chương trình thu âm : Mặc dù các từ tiếng Việt cũng như các lời chào và tạm biệt đã được thu âm sẵn trước khi tới tay người sử dụng, chương trình thông báokếtquảhọctập vẫn kèm thêm một chương trình thu âm nhằm cho phép người dùng thu âm lại nếu cần. Ngoài chức năng thu, chương trình còn cho phép phát lại hoặc xóa bất cứ từ nào đã thu âm. Nếu một từ đã có mà ta thu lại thì từ cũ đã thu âm sẽ bị thay bằng từ mới vừa thu âm. Để thu âm thì người dùng cần trang bị một micro loa nối vào soundcard. Các chức năng của chương trình thu âm : 1. Thu : - Các thông số cần cung cấp cho header hoàn toàn giống như đã nêu ở phần 2 - Để điều khiển soundcard dùng để thu âm, ta gọi hàm waveInOpen() với ID của thiết bị thu âm thanh sẽ được gán bằng hằng WAVE_MAPPER và chương trình sẽ tự động chọn thiết bị thu âm thanh phù hợp. - Tiếp đó ta phải tạo ra vùng đệm để lưu trữ các mẫu âm thanh thu được. Vùng đệm này có kích thước tối đa được tính như sau : BufferSize = MaxTime * Số mẫu trên 1giây * Số byte trên 1 mẫu Với MaxTime được quy định là 90 giây. Khi thời gian thu đến 90 giây thì chương trình sẽ ngừng thu và ghi dữ liệu vừa thu vào file. - Sau đó, dùng 2 hàm waveInPrepareHeader() và waveInAddBuffer() để nạp header chuẩn bị cho việc thu. Khi đã sẵn sàng, hàm waveInStart() sẽ bắt đầu công việc thu âm. Từ lúc này mọi âm thanh thu vào sẽ được lưu vào vùng đệm. - Khi ngừng thu, chương trình sẽ kiểm tra từ được nhập vào đã có chưa, nếu có thì sẽ xóa phần dữ liệu tiếngnói cũ và ghi lại dữ liệu mới vào cuối file *.sam tương ứng, đồng thời cập nhật lại 2 giá trị FileOffset và DataSize trong bảng chỉ mục. Song song đó, chương trình cũng sẽ cập nhật lại FileOffset và DataSize của các từ trong nhóm mà có FileOffset > FileOffset của từ vừa cập nhật. Đó là vì khối dữ liệu tương ứng trong file *.sam đã bị xóa (khối dữ liệu mới được để ở cuối file) nên toàn bộ các khối dữ liệu phía dưới sẽ được đôn lên, dẫn đến FileOffset của các từ phía dưới thay đổi theo. Còn FileOffset của các từ phía trên không thay đổi Như thế các FileOffset của các từ phía dưới sẽ được cập nhật lại như sau: FileOffset = FileOffset - DataSize của từ vừa xóa Nếu từ này chưa có thì dữ liệu tiếngnói sẽ được ghi vào cuối file *.sam tương ứng , đồng thời thêm từ này và 3 giá trị FileOffset , DataSize và tên nhóm vào bảng chỉ mục. Tên nhóm sẽ là chữ cái cơ bản dựa vào ký tự đầu tiên của từ vừa thu. - Tuy nhiên thường trước và sau khi khi thu âm sẽ có một khoảng im lặng. Khi phát ra một câu gồm nhiều từ ghép lại, nếu khoảng im lặng này quá lâu sẽ làm ngắt quãng câu nói. Do đó, trước khi ghi dữ liệu tiếngnói vào file *.sam , chương trình sẽ cắt bỏ những mẫu âm thanh nào có giá trị nằm trong khoảng 7Ah đến 86h. 2. Phát : - Quá trình phát ở đây tương tự như quá trình phátquađiện thoại, chỉ khác ở chỗ thiết bị xuất âm thanh là soundcard. Lúc này ID của thiết bị phát âm thanh sẽ được gán bằng hằng WAVE_MAPPER và chương trình sẽ tự động chọn thiết bị phát âm thanh phù hợp. 3. Xóa : - Trước tiên chương trình sẽ đọc 2 giá trị FileOffset và DataSize tương ứng với từ cần xóa. Dựa vào đó chương trình sẽ định vị được khối dữ liệu tiếngnói cần loại bỏ trong file *.sam tương ứng. - Chương trình sẽ đọc lại 2 khối dữ liệu trước và sau khối dữ liệu cần cắt bỏ , sau đó ghi ra một file tạm, xóa file *.sam , sau đó đổi tên file tạm trở thành *.sam đó - Lúc này trong bảng chỉ mục, FileOffset của các từ được thu trước đó không còn đúng so với file *.sam mới nữa. Vì vậy, ta phải tiến hành cập nhật lại những từ nào thuộc cùng nhóm với từ vừa xóa và FileOffset có lớn hơn FileOffset của từ vừa xóa, tương tự như trong phần thu một từ đã có. 4. Chèn khoảng im lặng : - Có một số từ cần thêm một khoảng thời gian im lặng ở đầu hoặc cuối từ vừa thu . Vì vậy chương trình cung cấp thêm chức năng này - Dựa vào thời gian im lặng đầu và cuối (theo mili giây) do người dùng nhập vào, chương trình sẽ tính toán kích thước dữ liệu mới như sau : DataSize= DataSize + (TgDau+TgCuoi)/1000*11500 - Số mẫu âm thanh cần thêm vào đầu và cuối được tính : SoMauDau = TgDau/1000*11500 SoMauCuoi = TgCuoi/1000*11500 - Tất cả các mẫu âm thanh được thêm vào sẽ được gán giá trị 80h. - Bây giờ ta chỉ việc đọc khối dữ liệu tiếngnói cũ vào bộ nhớ, sau đó xóa khối dữ liệu này trong file *.sam, cuối cùng ghi lại vào cuối file này các mẫu im lặng đầu, khối dữ liệu tiếngnói cũ trong bộ nhớ và các mẫu im lặng cuối. đồng thời cập nhật lại 2 giá trị FileOffset và DataSize của từ đó vào bảng chỉ mục và FileOffset của các từ phía trước bị thay đổi. . TỔ CHỨC TRUY XUẤT VÀ PHÁT FILE TIẾNG NÓI Sau khi đã tìm kiếm dữ liệu trong cơ sở dữ liệu , công việc cuối cùng là thông báo kết quả bằng giọng nói qua điện. việc cấp phát bộ nhớ , định vị khối dữ liệu tiếng nói trong file *.sam cho đến việc chọn thiết bị âm thanh và phát tiếng nói đi, hoặc thu tiếng nói vào vùng