MỤC LỤC
LỜI NÓI ĐẦU 1
PHẦN 1 GIỚI THIỆU ĐỀ TÀI 2
I. Đặt vấn đề : 3
II. Môi trường lập trình : 4
III. Giới thiệu về hoạt động của chương trình : 4
PHẦN 2 CƠ SỞ LÝ THUYẾT 6
Chương 1 GIAO TIẾP GIỮA ĐIỆN THOẠI VÀ MÁY TÍNH 7
I. Sự cần thiết của modem : 7
II. Giao tiếp lập trình ứng dụng cho hệ thống điện thoại - TAPI (Telephony Application Programming Interface) : 8
II.1. Một số khái niệm trong mô hình TAPI : 8
II.1.1. TAPI là gì ? 8
II.1.2. Chương trình ứng dụng TAPI : 9
II.1.3. TAPI DLL (Dynamic link library - Thư viện liên kết động) : 9
II.1.4. MSP (Media Service Provider ) : 10
II.1.5. MSPI (Media Service Provider Interface) : 11
II.1.6. TAPI Server : 11
II.1.7. TSP (Telephony Service Provider ) : 11
II.1.8. TSPI (Telephony Service Provider Interface) : 11
II.1.9. Service Providers : 12
II.2. Mô hình lập trình cho hệ thống điện thoại : 13
II.3. Mô hình ứng dụng TAPI : 14
II.4. Các phiên bản TAPI : 16
III. DTMF trong hệ thống điện thoại : 18
Chương 2 TỔNG QUAN VỀ CƠ SỞ DỮ LIỆU 19
I. Cơ sở dữ liệu trong thế giới hiện đại : 19
II. Sơ lược về ODBC (Open DataBase Connectivity) : 20
III. Các cách truy xuất cơ sở dữ liệu : 20
III.1. DAO (Data Access Objects): 20
III.2. RDO (Remote Data Object) : 20
III.3. ADO (ActiveX Data Objects) : 21
IV. Sơ lược về SQL (Structured Query Language) : 21
Chương 3 SƠ LƯỢC VỀ TIẾNG NÓI VÀ CÁC PHƯƠNG PHÁP TỔNG HỢP TIẾNG NÓI 23
I. Sơ lược về tiếng nói : 23
I.1. Đặc tính chung của tiếng nói : 23
I.2. Công nghệ Text–to–speech dùng để tổng hợp tiếng nói : 25
I.3. Sự cần thiết của công nghệ Text–to–speech (TTS) : 25
II. Các phương pháp tổng hợp tiếng nói trong hệ thống tiếng Việt : 26
II.1. Sự cần thiết của việc tổng hợp tiếng nói : 26
II.2. Các tiêu chuẩn cần thỏa mãn khi tổng hợp tiếng nói : 26
II.3. Các phương pháp tổng hợp tiếng nói cho tiếng Việt : 27
III. Giới thiệu về file Wave (*.wav) : 30
III.1. Khái niệm về file Wave và file RIFF : 30
III.2. Cấu trúc file Wave : 32
III.2.1. Chunk Format : 32
III.2.2. Chunk Data : 33
IV. Các phương pháp phát một file Wave : 36
IV.1. Dùng hàm sndPlaySound hoặc PlaySound : 36
IV.2. Dùng MCI (Media Control Interface) : 36
IV.3. Dùng các hàm cấp thấp của Windows : 36
V. Cách đọc file Wave vào bộ nhớ : 38
VI. Phương pháp thu âm : 39
PHẦN 3 THIẾT KẾ HỆ THỐNG 41
Chương 1 SƠ ĐỒ HOẠT ĐỘNG CỦA HỆ THỐNG 42
I. Lưu đồ giải thuật : 42
II. Sơ đồ luồng hoạt động của hệ thống : 43
Chương 2 HOẠT ĐỘNG GIAO TIẾP GIỮA ĐIỆN THOẠI VÀ MÁY TÍNH 46
I. Khởi tạo đường truyền (line) : 46
II. Kiểm tra đường truyền (line) hợp lệ : 47
III. Mở line : 47
IV. Đăng ký các sự kiện cho TAPI : 47
V. Kết nối với cuộc gọi đến : 47
VI. Thu nhận các mã DTMF : 48
VII. Kết thúc cuộc gọi : 48
VIII. Đóng line hiện tại : 49
IX. Kết thúc TAPI : 49
Chương 3 THIẾT KẾ VÀ TRUY XUẤT CƠ SỞ DỮ LIỆU 50
I. Sơ lược về cơ sở dữ liệu : 50
II. Mô hình quan niệm dữ liệu : 51
III. Hoạt động truy xuất cơ sở dữ liệu : 52
IV. Chương trình cập nhật dữ liệu : 52
Chương 4 TỔ CHỨC, TRUY XUẤT VÀ PHÁT FILE TIẾNG NÓI 55
I. Chọn phương pháp tổng hợp tiếng nói : 55
II. Chọn định dạng (format) cho file tiếng nói : 55
III. Cách tổ chức file tiếng nói : 56
IV. Chọn phương pháp phát và thu tiếng nói : 58
V. Đọc dữ liệu tiếng nói vào bộ nhớ : 58
VI. Phát dữ liệu tiếng nói từ vùng đệm : 59
VII. Chương trình thu âm : 59
PHẦN 4 VẬN HÀNH VÀ ĐÁNH GIÁ HỆ THỐNG 63
I. Vận hành : 64
I.1. Chưong trình chính : 64
I.2. Chưong trình cập nhật dữ liệu : 66
I.3. Chưong trình thu tiếng nói : 68
II. Đánh giá hệ thống : 69
PHẦN 5 KẾT LUẬN 71
PHỤ LỤC MÃ NGUỒN THỰC HIỆN MỘT SỐ CÔNG VIỆC CHÍNH 73
1. CHƯƠNG TRÌNH CHÍNH 74
2. CHƯƠNG TRÌNH CẬP NHẬT DỮ LIỆU 97
3. CHƯƠNG TRÌNH THU TIẾNG NÓI 103
TÀI LIỆU THAM KHẢO 113
MỤC LỤC 114
116 trang |
Chia sẻ: maiphuongdc | Lượt xem: 1479 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Khóa luận Chương trình Thông báo kết quả học tập qua điện thoại, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
ọn phiên bản đầu tiên là TAPI 1.4 . Phiên bản này được thiết kế theo kiến trúc ngôn ngữ C++ nên khi lập trình trong Visual Basic sẽ khó khăn hơn. Tuy nhiên, ứng dụng TAPI 1.4 sẽ có thể chạy trên mọi phiên bản Windows 32-bit và không đòi hỏi cấu hình cao, trong khi ứng dụng TAPI 3 chỉ chạy trên Windows 2000/XP.
Để sử dụng các hàm của TAPI, các hàm này phải được khai báo trước trong một module giống như các hàm API khác của Windows. Sau đây là các bước để tạo một kết nối với cuộc gọi tới :
Khởi tạo đường truyền (line) :
Mọi hoạt động giao tiếp giữa điện thoại và máy tính muốn diễn ra thì trước hết cần phải khởi tạo đường truyền giữa máy tính và điện thoại. Ở đây, modem sẽ là thiết bị trung gian giữa máy tính và điện thoại. Vì thế, đường truyền mà ta cần quan tâm chính là modem. Một máy tính có thể có gắn nhiều hơn một modem. Vì vậy, trước hết ta cần phải sử dụng hàm lineInitialize() để lấy về tổng số đường truyền (line) mà nó phát hiện có trong máy.
Cũng từ đây, ta sẽ chỉ định hàm nào sẽ tiếp nhận các sự kiện được sinh ra bởi các hoạt động của TAPI (ví dụ như khi chuông reo, khi có mã DTMF được gửi tới, vv...). Hàm này được gọi là lineCallBack() , do ta tự định nghĩa. Sau đó, ta sẽ lấy địa chỉ của hàm này và đưa vào hàm lineInitialize() như là một tham số của hàm lineInitialize()
Sau khi đã khởi tạo thành công, lineInitialize() sẽ trả về một handle của TAPI đã nạp vào bộ nhớ. Handle này sẽ được sử dụng trong suốt quá trình hoạt động của hệ thống.
Kiểm tra đường truyền (line) hợp lệ :
Vì mục đích của ta là lấy được các modem hiện có nên ta cần phải kiểm tra những line nào thích hợp. Sau khi có được tổng số các line, ta duyệt qua từng line để tìm line thích hợp bằng cách kết hợp gọi 2 hàm lineNegotiateAPIVersion() và lineGetDevCaps().
Ứng với mỗi line, hàm lineNegotiateAPIVersion() sẽ trả về con số phiên bản (version) API mà tương thích với phiên bản của TAPI hiện đang sử dụng. Với con số phiên bản lấy được, hàm lineGetDevCaps() sẽ kiểm tra và trả về những chức năng được hỗ trợ trên line này. Từ đây, ta sẽ chọn được các line thích hợp chính là các modem hiện có.
Mở line :
Để bắt đầu cho sự bắt tay làm việc giữa máy tính và điện thoại, sau khi đã chọn được line thích hợp, ta mở line đó bằng hàm lineOpen(). Nếu mở thành công. nó sẽ trả về một handle của line được mở. Với handle này, ta sẽ điều khiển mọi hoạt động diễn ra trên line này.
Đăng ký các sự kiện cho TAPI :
Mọi sự kiện liên quan đến TAPI (ví dụ chuông reo, phát/nhận các mã DTMF, ... ) được gửi đến hàm lineCallBack() đều phải được đăng ký sau khi mở line. Để đăng ký, ta gọi hàm lineSetStatusMessages() và đưa vào giá trị mô tả các sự kiện mà ta cần. Giá trị này thực chất là một dãy các 32 bit liên tiếp, mỗi bit tương ứng với một sự kiện của TAPI.
Kết nối với cuộc gọi đến :
Khi có tín hiệu gọi tới (chuông reo), thông điệp LINEDEVSTATE_RINGING sẽ được gửi tới trong hàm lineCallBack() cùng với một handle của cuộc gọi đó. Nếu số tiếng chuông reo bằng với một con số mà ta quy định trước đó thì ta sẽ gọi hàm lineAnswer() với handle vừa nhận được để kết nối với cuộc gọi đến. Công việc này tương đương với việc nhấc máy điện thoại lên để trả lời người gọi tới. Vì vậy, kể từ lúc này, ta có thể gửi và nhận các mã DTMF hoặc gửi đi một chuỗi âm thanh nào đó và người gọi có thể nghe được.
Thu nhận các mã DTMF :
Một khi đã kết nối thành công với cuộc gọi đến, ta gọi hàm lineMonitorDigits() để giám sát và thu nhận các mã DTMF được gửi tới. Có thể nói đây là phần trung tâm của hệ thống, vì hệ thống sẽ tiếp nhận yêu cầu của người gọi thông qua các mã DTMF này.
Khi người gọi nhấn các phím trên điện thoại thì thông điệp LINE_MONITORDIGITS sẽ được gửi tới hàm lineCallBack() cùng với các mã DTMF tương ứng như sau :
Phím
Mã DTMF (Hex)
0
30
1
31
2
32
3
33
4
34
5
35
6
36
7
37
8
38
9
39
*
2A
#
23
Kết thúc cuộc gọi :
Khi người gọi chọn yêu cầu “kết cầu thúc cuộc gọi” từ menu hoặc người gọi gác máy bất kỳ lúc nào, ta phải gọi hàm lineDrop() để hủy cuộc gọi hiện tại đang kết nối và hàm lineDeallocateCall() để giải phóng cuộc gọi khỏi bộ nhớ. Bây giờ hệ thống sẽ trở về trạng thái chờ đợi cuộc gọi khác đến.
Đóng line hiện tại :
Nếu muốn chọn modem khác để hoạt động, hoặc kết thúc hoạt động của hệ thống, ta cần gọi hàm lineClose() để đóng line hiện tại đang được mở. Đây là điều nên làm vì nó sẽ hoàn trả tài nguyên về cho máy.
Kết thúc TAPI :
Sau khi khởi tạo các line bằng hàm lineInitialize(), một số tài nguyên hệ thống sẽ bị chiếm dụng suốt phiên làm việc của TAPI. Khi thoát chương trình, ta gọi hàm lineShutdown() để kết thúc phiên làm việc đó, đồng thời giải phóng các tài nguyên hệ thống.
Chương 3 THIẾT KẾ VÀ TRUY XUẤT CƠ SỞ DỮ LIỆU
Sơ lược về cơ sở dữ liệu :
Một số đặc điểm của cơ sở dữ liệu dùng trong chương trình này :
Mô hình cơ sở dữ liệu được chọn là mô hình cơ sở dữ liệu quan hệ
Được cài đặt trong hệ quản trị cơ sở dữ liệu Microsoft Access 97.
Phông chữ được sử dụng trong cơ sở dữ liệu này thuộc bảng mã VNI Windows (2 byte). Mặc dù bảng mã này sử dụng 2 byte để mã hóa một ký tự tiếng Việt , nhưng toàn bộ các ký tự hoa và ký tự thường đều cùng nằm trong một phông chữ. Còn bảng mã TCVN3 (ABC) chỉ sử dụng 1 byte cho mỗi ký tự tiếng Việt nhưng lại phân ra thành 2 phông khác nhau : một phông cho chữ hoa và một phông cho chữ thường. Điều này sẽ gây khó khăn cho cả việc thiết kế và việc nhập dữ liệu từ người dùng. Bảng mã Unicode cũng sử dụng 2 byte cho mỗi ký tự tiếng Việt nhưng giá trị chênh lệch mã ASCII giữa ký tự hoa và ký tự thường không đồng đều. Còn bảng mã VNI thì giá trị chênh lệch này luôn là 20h. Ví dụ : chữ “A” có mã ASCII là 41h, chữ “a” có mã ASCII là 61h ; chữ “B” có mã ASCII là 42h, chữ “b” có mã ASCII là 62h . Sự chênh lệch đồng đều này sẽ thuận lợi cho việc chuyển đổi giữa chữ hoa và chữ thường.
Mô hình quan niệm dữ liệu :
Hoạt động truy xuất cơ sở dữ liệu :
Đối tượng truy xuất dữ liệu được sử dụng trong chương trình này là Microsoft DAO 3.6. Khi hệ thống bắt đầu hoạt động, cơ sở dữ liệu sẽ được mở bằng phương thức OpenDatabase() để sẵn sàng phục vụ cho các hoạt động truy xuất dữ liệu. Và khi hệ thống ngừng hoạt động, cơ sở dữ liệu cũng sẽ được đóng lại bằng phương thức Close.
Mỗi khi có người gọi tới muốn nghe một kết quả nào đó thì hệ thống sẽ yêu cầu người gọi cung cấp một số thông số cần thiết như : mã số học sinh, tên tháng hoặc tên học kỳ muốn biết, vv.... Từ những thông số này, hệ thống sẽ tạo ra một câu truy vấn SQL để tìm kết quả trong cơ sở dữ liệu và lấy về thông qua phương thức OpenRecordset(). Nếu không tìm thấy kết quả nào, thuộc tính EOF của biến đối tượng Recordset sẽ mang giá trị TRUE và ngược lại sẽ mang giá trị FALSE.
Vì mỗi lần truy vấn, hệ thống chỉ truy vấn trên một học sinh và được giới hạn bởi một tháng hoặc một học kỳ nào đó nên tốc độ truy vấn khá nhanh, người gọi có thể nhận được kết quả ngay mà không phải chờ lâu. Ngoài ra, vì cơ sở dữ liệu được cài đặt bằng Access nên được hỗ trợ cơ chế cập nhật dữ liệu động, hệ thống có thể hoạt động liên tục kể cả lúc dữ liệu đang được cập nhật vào.
Chương trình cập nhật dữ liệu :
Nhằm tạo điều kiện thuận lợi cho người quản trị hệ thống, ngoài chương trình chính là thông báo kết quả còn có thêm một chương trình phụ kèm theo để cập nhật dữ liệu vào cơ sở dữ liệu. Người dùng có thể mở chương trình này thông qua menu của chương trình chính, kể cả lúc hệ thống đang hoạt động.
Các phần cập nhật dữ liệu trong chương trình này đều sử dụng đối tượng truy cập dữ liệu Microsoft DAO 3.6 . Mọi thao tác với cơ sở dữ liệu đều thông qua các phương thức có sẵn của DAO , đồng thời kết hợp với các câu lệnh SQL.
Trước khi thực hiện cập nhật dữ liệu, người sử dụng cần phải chọn năm học và lớp ở năm học đó. Mọi thao tác cập nhật sẽ ảnh hưởng tới một trong các học sinh thuộc lớp đó. Chương trình cập nhật dữ liệu tổ chức dạng menu gồm 8 phần :
Học sinh :
Dùng để cập nhật các thông tin chi tiết về học sinh
Khi thêm một học sinh mới vào lớp được chọn, chương trình sẽ tự sinh ra một mã số cho học sinh đó. Mã số này được đánh theo số thứ tự tăng dần. Vì vậy, chương trình sẽ lấy mã số của học sinh cuối cùng trong bảng HOCSINH rồi tăng lên một đơn vị để làm mã số cho học sinh mới sắp sửa thêm vào. Mã số này sẽ luôn được chương trình quản lý, người dùng không thể tự ý thay đổi.
Ở phần ngày sinh, để người sử dụng không phải lúng túng trong việc gõ ngày tháng theo kiểu của Mỹ (tháng/ngày/năm) , em đã viết thêm một hàm chuyển từ kiểu ngày/tháng/năm sang kiểu tháng/ngày/năm để lưu vào cơ sở dữ liệu. Nhờ đó, người dùng có thể gõ vào ngày sinh bình thường theo kiểu Việt Nam. Song song đó, em cũng phải viết thêm hàm chuyển ngược lại từ tháng/ngày/năm sang ngày/tháng/năm nhằm hiển thị ra màn hình ngày sinh được lấy từ cơ sở dữ liệu.
Trong phần nơi sinh, tên của tất cả 61 tỉnh/thành phố đã được nạp sẵn vì một học sinh có thể học tại trường thuộc tỉnh/thành phố này nhưng lại được sinh ra ở một tỉnh/thành phố khác.
Ứng với mỗi học sinh được thêm vào bảng HOCSINH thì mã số của học sinh đó cùng với mã lớp và mã năm học sẽ được cập nhật vào trong bảng HS_LOP để biết học sinh đó thuộc lớp nào ở năm học nào.
Vì mỗi học sinh sẽ được gán cho một mã số duy nhất nên mỗi học sinh chỉ được thêm vào trong bảng HOCSINH một lần. Do đó, nút “Chuyển lớp” được dùng để chuyển một học sinh từ lớp này sang lớp khác ở cùng năm học. Nhờ đó mà các kết quả trước đó của học sinh sẽ vẫn được bào toàn. Công việc này đơn giản chỉ là cập nhật lại mã lớp mới thay cho mã lớp cũ.
Tương tự , nút “Học sinh cũ” sẽ cho phép thêm tất cả các học sinh thuộc một lớp nào đó ở năm học trước vào lớp hiện tại ở năm học hiện tại. Lúc này chương trình chỉ cập nhật các học sinh vào bảng HS_LOP.
Nghỉ học :
Dùng để thêm các lần nghỉ học của học sinh
Phần nhập ngày nghi học được thiết kế tương tự như phần ngày sinh ở trên
Dữ liệu sẽ được ghi vào bảng NGHIHOC
Lỗi vi phạm :
Dùng để thêm các lần vi phạm nội quy của học sinh
Phần nhập ngày vi phạm được thiết kế tương tự như phần ngày sinh ở trên
Dữ liệu sẽ được ghi vào bảng VIPHAM
Điểm kiểm tra :
Dùng để thêm điểm kiểm tra của học sinh
Dữ liệu sẽ được ghi vào bảng KQKIEMTRA
Điểm thi :
Dùng để thêm điểm thi của học sinh
Dữ liệu sẽ được ghi vào bảng KQTHI
Kết quả cuối tháng :
Dùng để thêm kết quả ở cuối tháng của học sinh
Dữ liệu sẽ được ghi vào bảng KQTHANG
Kết quả cuối học kỳ :
Dùng để thêm kết quả ở cuối học kỳ của học sinh
Dữ liệu sẽ được ghi vào bảng KQHOCKY
Kết quả cuối năm học :
Dùng để thêm kết quả ở cuối năm học của học sinh
Dữ liệu sẽ được ghi vào bảng KQNAMHOC
Ngoài ra, nhằm tạo sự linh động cho người dùng, chương trình còn cho phép thêm mới một số loại dữ liệu sau : năm học, lớp, lý do nghỉ học, lý do vi phạm nội quy, môn học, loại kiểm tra.
Chương 4 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 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ếng nói tương ứng và phát qua điện thoại . Trước hết, em sẽ giới thiệu về cách tổ chức và lưu trữ tiếng nói.
Chọn phương pháp tổng hợp tiếng nó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.
Chọn định dạng (format) cho file tiếng nó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ếng nó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ếng nó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
Cách tổ chức file tiếng nó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ông báo lỗi , vv... Có hai cách tổ chức file tiếng nói :
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át file .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át file .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ếng nó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.
Tổ chức thành các file dữ liệu tiếng nó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ếng nó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ếng nó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ếng nó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 file và 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ống và 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ếng nó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ông bá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ông bá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 .
Chọn phương pháp phát và thu tiếng nói :
Vì các dữ liệu tiếng nói được ta tự tổ chức và lưu trữ trong các file *.sam nên để phát và 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ế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 đệm và lưu vào file *.sam.
Đọc dữ liệu tiếng nó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ếng nó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ếng nói củ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()
Phát dữ liệu tiếng nó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ết nối thông qua 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ếng nó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.
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áo kết quả học tậ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 :
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ếng nó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ếng nó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ếng nó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.
Phát :
Quá trình phát ở đây tương tự như quá trình phát qua đ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.
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ếng nó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ó.
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ếng nó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ếng nó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.
PHẦN 4VẬN HÀNHVÀ ĐÁNH GIÁHỆ THỐNG
Vận hành :
Chưong trình chính :
Lần đầu tiên chạy chương trình, hộp thoại cấu hình sẽ hiện ra cho người dùng chọn modem, số tiếng chuông reo và thời gian rỗi tối đa.
Khi bắt đầu chạy, chương trình có giao diện như sau :
Đây là trạng thái của hệ thống đang tắt, nghĩa là mọi cuộc gọi tới sẽ không được trả lời. Sau khi nhấn nút “Chờ cuộc gọi” thì hệ thống sẽ bắt đầu hoạt động, chờ tín hiệu của cuộc gọi tới :
Nếu có người gọi tới, hình ảnh minh họa tiếng chuông đang reo của chiếc điện thoại như sau :
Sau một số tiếng chuông reo được đặt trước thì hệ thống sẽ “nhấc máy” để trả lời người gọi :
Sau khi người gọi kết thúc cuộc gọi, hệ thống sẽ trở lại trạng thái 2, tức là chờ đợi cuộc gọi khác.
Khi hệ thống đang hoạt động, bất kỳ lúc nào người sử dụng nhấn nút “Dừng” thì hệ thống sẽ ngắt kết nối (nếu có) và ngừng hoạt động ngay. Tóm lại, hệ thống sẽ ở một trong 4 trạng thái trên.
Ngoài ra, người dùng có thể mở chương trình cập nhật dữ liệu và thu tiếng nói thông qua menu “Cập nhật dữ liệu” :
Chưong trình cập nhật dữ liệu :
Chương trình chia làm 8 phần cập nhật sẽ được chọn thông qua menu :
Phần “Học sinh” sẽ cho phép thêm, sửa, xóa các học sinh thuộc lớp và năm học hiện tại được chọn :
Trong phần này, nút “Chuyển lớp” cho phép chuyển một học sinh từ lớp hi