Tài liệu Đề tài Xây dựng ứng dụng truyền thông âm thanh trên mạng cục bộ: Đề tài :
Xây dựng ứng dụng truyền thông
âm thanh trên mạng cục bộ
LỜI NÓI ĐẦU
Trong những năm gần đây, mạng máy tính ngày càng trở nên phổ biến. Việc liên kết
các máy tính trên môi trường mạng cũng như liên kết các mạng lại với nhau đem lại cho
chúng ta nhiều lợi ích trong công việc cũng như trong việc học tập nghiên cứu, giải trí.
Chúng ta có thể sử dụng các tài nguyên sẵn có được chia xẻ như file server, printer, máy
fax, ... môi trường mạng còn là một môi trường thông tin nhanh chóng và tiện lợi nhờ vào
các cơ chế truyền thông trên mạng như : e-mail, www ...
Bên cạnh đó, tốc độ phát triển của máy tính PC cũng rất nhanh chóng. Các kỹ thuật
hiện đại đã giúp tạo ra các máy PC với tốc độ tính toán nhanh hơn, bộ nhớ lớn hơn và khả
năng xử lý của nó cũng ngày càng đa dạng hơn trong khi giá thành ngày càng rẻ hơn. Một
trong những khả năng ưu việt của máy PC hiện nay là hỗ trợ multimedia. Các máy PC ngày
nay giao tiếp với con người không chỉ bằng text mà còn kết ...
131 trang |
Chia sẻ: haohao | Lượt xem: 1096 | Lượt tải: 0
Bạn đang xem trước 20 trang mẫu tài liệu Đề tài Xây dựng ứng dụng truyền thông âm thanh trên mạng cục bộ, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Đề tài :
Xây dựng ứng dụng truyền thông
âm thanh trên mạng cục bộ
LỜI NÓI ĐẦU
Trong những năm gần đây, mạng máy tính ngày càng trở nên phổ biến. Việc liên kết
các máy tính trên môi trường mạng cũng như liên kết các mạng lại với nhau đem lại cho
chúng ta nhiều lợi ích trong công việc cũng như trong việc học tập nghiên cứu, giải trí.
Chúng ta có thể sử dụng các tài nguyên sẵn có được chia xẻ như file server, printer, máy
fax, ... môi trường mạng còn là một môi trường thông tin nhanh chóng và tiện lợi nhờ vào
các cơ chế truyền thông trên mạng như : e-mail, www ...
Bên cạnh đó, tốc độ phát triển của máy tính PC cũng rất nhanh chóng. Các kỹ thuật
hiện đại đã giúp tạo ra các máy PC với tốc độ tính toán nhanh hơn, bộ nhớ lớn hơn và khả
năng xử lý của nó cũng ngày càng đa dạng hơn trong khi giá thành ngày càng rẻ hơn. Một
trong những khả năng ưu việt của máy PC hiện nay là hỗ trợ multimedia. Các máy PC ngày
nay giao tiếp với con người không chỉ bằng text mà còn kết hợp tất cả các phương tiện khác
như tiếng nói, hình ảnh.
Việc đưa kỹ thuật multimedia vào các ứng dụng truyền thông trên mạng giúp chúng
ta tạo ra nhiều ứng dụng phong phú hơn. Chẳng hạn hộp thư điện tử ngày nay có thể không
chỉ là văn bản mà còn bao gồm tiếng nói, hình ảnh. Các trang web trở nên sinh động hơn
hẳn khi kèm theo kỹ thuật multimedia. Bên cạnh đó, chúng ta có thể thiết kế các ứng dụng
tiện ích như Video conference, voice mail...
Thông qua chương trình này, người sử dụng có thể trao đổi thông tin với nhau bằng
tiếng nói. Chương trình này đã được hiện thực rất nhiều trong các lĩnh vực thông tin như
điện thoại, viễn thông, máy tính . . . Tuy nhiên nó chưa được áp dụng và phát triển rọng rãi
như trên các lĩnh vực thông tin khác do sự hạn chế của thiết bị. Ngày nay, khi công nghệ
thông tin đã phát triển thì việc hiện thực chương trình này là hoàn toàn có thể. Ứng dụng
trong nhiều lĩnh vực khác nhau như :
•Việc dạy học từ xa.
•Việc chẩn đoán, chữa bệnh từ xa.
•Hội thảo, thảo luận theo nhóm.
•Công cụ trao đổi thông tin bằng hình ảnh và âm thanh.
Mục tiêu của đồ án tốt nghiệp là tìm hiểu các mô hình và công nghệ truyền âm thanh
trên mạng máy tính, trên cơ sở đó xây dựng ứng dụng truyền thông âm thanh thoại trên
mạng cục bộ. Đồ án sẽ xây dựng thử nghiệm một hệ thống cho phép trao đổi thông tin bằng
tiếng nói thoại, tương tác điểm – điểm trên mạng LAN. Đồ án được trình bày gồm 5 chương
với bố cục như sau :
Chương I : Tìm hiểu các mô hình điện thoại qua mạng, Từ đó đưa ra mô hình sẽ thực thi
trong đồ án này.
Chương II: Giới thiệu chung về các giao thức truyền thông trên mạng Internet và vào khảo
sát cụ thể các giao thức này.
Chương III : Giới thiệu các chuẩn mã hoá và nén âm thanh.
Chương IV : Tìm hiều môi trường lập trình SDK Windows và ứng dụng trên mạng.
Chương V : Thiết kế chương trình truyền tiếng nói qua mạng LAN thông qua sự trợ giúp
của công cụ SDK. Đánh giá và các kết quả thử nghiệm.
Phần kết luận : Nêu những kết quả của đề tài và hướng nghiên cứu hướng phát triển tiếp
theo.
Việc nghiên cứu lý thuyết một cách hệ thống và xây dựng chương trình phần mềm
đòi hỏi phải đầu tư nhiều thời gian. Với thời gian có hạn cho nên bài luận văn này của em
không tránh khỏi những thiếu sót, em rất mong được sự chỉ dẫn thêm của thầy cô và các
bạn.
Nhân đây, em xin chân thành cám ơn đến PGS - TS Nguyễn Thị Hoàng Lan, người
trực tiếp hướng dẫn em hoàn thành cuốn luận văn này. Em xin chân thành cám ơn các thầy
cô trong khoa Công nghệ thông tin Trường ĐH Bách Khoa HN, Trường ĐH Thủy Sản và
toàn thể các bạn đã giúp đỡ em hoàn thành cuốn luận văn này.
CHƯƠNG I
TÌM HIỂU MÔ HÌNH ĐIỆN THOẠI QUA MẠNG
I.1 CÁC MÔ HÌNH ĐIỆN THOẠI
I.1.1 MÁY TÍNH ĐẾN MÁY TÍNH
Trong mô hình này cả hai thuê bao đều sử dụng máy tính được nối vào mạng IP như
một thiết bị đầu cuối. Tiếng nói được mã hoá sau đó là nén và quá trình nhận dữ liệu hoàn
toán giống nhưng với quy trình ngược lại là giải nén, giải mã bằng phần mềm. Trong mô
hình này đòi hỏi cả hai thuê bao cần phải có soundcard, microphone, loa và phần mềm
giống nhau.[1]
Hình I.1 : Mô hình PC - PC
I.1.2 MÁY TÍNH ĐẾN ĐIỆN THOẠI HOẶC ĐIỆN THOẠI ĐẾN MÁY TÍNH
Trong mô hình này, một thuê bao sử dụng máy tính nối mạng với mạng còn thuê bao
kia sử dụng điện thoại trong mạng PSTN/ISDN/GSM/TDM. Sử dụng một gateway để
chuyển tiếng nói trên mạng IP thành tiếng nói trên mạng PSTN và trao đổi thông tin giữa
hai mạng trên. Như vậy, ở đây máy tính phải đầy đủ các thiết bị như Soundcard, loa,
Máy tính Máy tính IP
microphone và phần mềm thông qua server của mạng IP để có thể kết nối với mạng PSTN
thông qua Geteway.
Hình I.2 : Mô hình Máy tính – Điện thoại
I.1.3 ĐIỆN THOẠI ĐẾN ĐIỆN THOẠI
Trong mô hình này, cả hai thuê bao đều sử dụng điện thoại bình thường và mạng IP
được sử dụng trong trường hợp cuộc gọi đường dài. Gateway được sử dụng ở cả hai đầu để
chuyển đổi dữ liệu giữa các mạng với nhau.[1]
Hình I.3 : Mô hình Điện thoại – Điện thoại
Máy tính Điện thoại IP
G
at
ew
ay
PSTN
Đ
iệ
n
th
oạ
i
IP
G
at
ew
ay
PSTN PSTN
G
at
ew
ay
Đ
iệ
n
th
oạ
i
I.2 YÊU CẦU ỨNG DỤNG TRUYỀN ÂM THANH TRÊN MẠNG LAN
Trong phần này sẽ phân tích các yêu cầu xây dựng ứng dụng truyền âm thanh trên
mạng LAN : Từ ba mô hình trên em nhận thấy mô hình 2 và 3 đòi hỏi quá trình nghiên cứu
và thiết bị phức tạp, đòi hỏi phải đầu tư công nghệ mới có thể thực hiện được. Nên trong đồ
án này chỉ có thể thức hiện theo mô hình 1 là PC – PC, nó đơn giản hơn và không cần đầu
tư thiết bị mới, có thể tận dụng các thiết bị có sẵn và điều quan trọng có thể thử nghiệm
hoàn chỉnh trong phạm vi đồ án.
Vấn đề đặt ra là với một hệ thống mạng LAN, WAN cùng với tài nguyên sẵn có của
nó xây dựng một chương trình truyền tiếng nói với thời gian thực cho phép từ máy này sang
máy khác (point to point) với một số các yêu cầu thích hợp giống như việc xử lý và truyền
tiếng nói trong thông tin liên lạc (điện thoại hữu tuyến). Chương trình sử dụng giao thức
TCP/IP là một giao thức phổ biến và tin cậy hiện nay để kết nối và truyền tiếng nói. Do sử
dụng giao thức TCP là giao thức có liên kết nên dẫn đến độ trễ rất lớn nhưng với ứng dụng
trong mạng LAN thì vẫn có thể chấp nhận được. Ngay khi tiếng nói được thu và có thể qua
một số các xử lý như mã hoá tiếng nói hoặc nén trên một máy, tiếng nói được truyền tới
máy cần kết nối và qua các xử lý ngược so với lúc thu như giải nén và giải mã để được phát
ra loa. Chương trình cho phép kết nối hai máy và tạo một mô hình điện thoại trên máy tính
như điện thoại hữu tuyến thông thường. Bất kỳ máy nào trong mạng cũng có thể ở chế độ
chờ hay chế độ chạy nền (background) gọi máy là P-SERVER; máy ở chế độ gọi (active)
gọi là P-CLIENT. Như vậy một máy trong mạng có thể là P-SERVER hoặc P-CLIENT.
Trên môi trường mạng, khi chúng ta muốn nói chuyện một người trên một máy nào đó,
chúng ta sẽ tiến hành việc gọi liên kết. Việc gọi liên kết được tiến hành bằng việc xác định
địa chỉ IP của máy mà chúng ta cần liên kết. Sau đó chúng ta sẽ chờ việc xác lập liên kết. Ở
máy được gọi sẽ có thông báo cho người sử dụng biết rằng có một người khác muốn nói
chuyện. Tùy theo người đó quyết định có chấp nhận hay không. Nếu chấp nhận thì liên kết
sẽ được xác lập và hai bên sẽ có thể tiến hành trao đổi thông tin với nhau.
Trong quá trình trao đổi thông tin, các máy sẽ truyền tiếng nói của người sử dụng
đồng thời nhận dữ liệu âm thanh của máy liên kết. Khi nói chuyện xong, liên kết sẽ bị hủy
bỏ và chương trình kết thúc. Nếu máy được gọi không có người trả lời thì sau thời gian chờ
vượt quá giới hạn thì liên kết cũng sẽ bị huỷ bỏ. Vì dữ liệu truyền nhận trong chương trình
là dữ liệu dạng liên tục của âm thanh cho nên có các yêu cầu đặt ra như sau: Bảo đảm tính
mạch lạc của dữ liệu. Tiếng nói trong quá trình thông tin phải rõ ràng, liền lạc, không bị
ngắt quãng.
Các yêu cầu trên đặt ra các nhiệm vụ mà chúng ta phải giải quyết trong việc xây
dựng chương trình. Đối với dữ liệu là âm thanh, chúng ta phải xem xét các thông số trong
quá trình lấy mẫu ở đầu vào. Các thông số đặc trưng như : tần số lấy mẫu, số bit biểu diễn
cho một điểm lấy mẫu, kênh lấy mẫu được sử dụng 1 kênh (mono) hay hai kênh (stereo). Do
đó chúng ta phải tổ chức kích thước buffer âm thanh sao cho phù hợp với việc truyền nhận
đạt tốc độ cao. Một vấn đề khác được đặt ra với dữ liệu âm thanh là việc nhận và phát ở đầu
ra, chúng ta phải quan tâm đến việc xử lý và loại bỏ các tín hiệu nhiễu giúp cho âm thanh
được rõ ràng, trung thực. Do việc truyền nhận dữ liệu là trên môi trường mạng nên chúng ta
phải quan tâm đến tốc độ, lưu lượng trao đổi dữ liệu, thời gian truyền nhận để đưa ra cách
giải quyết cho phù hợp. Ngoài các vấn đề chính ở trên, một số yêu cầu khác đặt ra cho ứng
dụng như : cơ chế tạo lập liên kết, việc chọn lựa các dạng format dữ liệu, định các thông số
thời gian. Tất cả các nhiệm vụ thực thi đều phải được thực hiện thông qua giao diện dễ dàng
cho người sử dụng.[3]
Trường hợp mở rộng hệ thống cho mạng Internet cần một kết nối giữa server mạng
với tổng đài mạng PABX, yêu cầu này đòi hỏi phức tạp hơn và cần có các trang thiết bị để
thử nghiệm. Bởi vậy trong đồ án này em chỉ nghiên cứu cách thức truyền tiếng nói trên
mạng nội bộ.
CHƯƠNG II
KHẢO SÁT CÁC GIAO THỨC TRUYỀN THÔNG
II.1 KHÁI NIỆM CƠ BẢN GIAO THỨC TCP/IP
Trong hệ thống mạng Internet, mỗi máy có một tên và một địa chỉ IP (Internet
Protocol). Tên hay địa chỉ IP đều xác định duy nhất một máy trong hệ thống mạng Internet.
Giữa tên máy và địa chỉ IP đều có thể chuyển đổi thông qua các hàm. Địa chỉ IP đều được
biểu diễn bằng một số 32 bits. Mỗi giao diện mạng trong một nút nếu có hỗ trợ một ngăn
xếp IP đều được gán một địa chỉ IP. Địa chỉ IP gồm 2 phần : chỉ số mạng(netid) và chỉ số
của máy chủ (hostid). Những bits quan trọng nhất được dùng để xác định số lượng bits dùng
cho netid và hostid. Có 5 lớp địa chỉ được xác định là A,B,C,D và E. Trong đó, lớp A,B,C
chứa địa chỉ có thể gián được. Lớp D dành riêng cho kỹ thuật Multicasting và được sử dụng
trong các giao thức đặc biệt để truyền thông điệp đến một nhóm nút được chọn lọc. Lớp E
dành riêng cho những ứng dụng trong tương lai.[5]
Hình II.1 Các lớp đại chỉ IP
Hình II.2 TCI/IP và mô hình OSI
Netid nhận dạng cho từng mạng riêng biệt. Các kiểu lớp địa chỉ IP cho thấy số lượng
mạng và số lượng nút của mỗi lớp khác nhau. Bảng dưới cho thấy số lượng mạng và số
lượng nút có thể của mỗi lớp địa chỉ :
Lớp địa chỉ Số lượng mạng Số lượng nút
A 127 16.777.241
B 16.383 65.534
C 2.097.151 254
Mạng lớp A dùng cho mạng diện rộng. Trường netid có 7 bits nên có thể có 127
mạng. Mạng lớp B là mạng có kích thước trung bình và thích hợp cho các tổ chức có quy
mô lớp và vừa. Mạng lớp C dùng trong cơ quan nhỏ, trong đó mỗi mạng chỉ có không hơn
254 nút.
Con số 32 bits biểu thị 4 chữ số thập phân tương ứng giá trị 4 byte tạo thành địa chỉ
IP. Những số thập phân cách nhau bởi dấu chấm (.). Một ví dụ về tên máy và địa chỉ IP của
máy :
Hostname : viethung
IP Address : 192.168.0.55
II.1.1 KHÁI NIỆM SOCKET
Socket là một đơn vị cấu trúc truyền thông 2 chiều. Chúng có thể đọc hay ghi lên nó.
Tuy nhiên mỗi socket là một thành phần của một mối nào đó giữa các máy trên mạng máy
tính và các thao tác đọc ghi chính là các thao tác trao đổi dữ liệu giữa các ứng dụng trên
nhiều máy khác nhau. Socket là điểm kết nối cuối cùng cho phép những ứng dụng gắn vào
mạng. Khái niệm socket được cung cấp bởi một thư viện chứa tất cả các hàm yêu cầu cho
bất kỳ chương trình mạng nào. Khi một ứng dụng yêu cầu các dịch vụ mạng, nó gọi quá
trình tự tập hợp các thư viện để quản lý hoạt động mạng. Hai loại socket có sẵn stream và
datagram.[5]
Những socket stream dùng cho TCP (Transmission Control Protocol), những socket
datagram dùng UDP (User Datagram Protocol).
Máy A Máy B
Port Mối nối Port
TCI/IP Interface
Hình II.3 Các socket và port trong mối nối TCP/IP
Số hiệu cổng Mô tả
0 Reserved
2 Management Entry
5 Remote Job Entry
7 Echo
9 Discard
11 Systat
13 Daytime
15 Netstat
17 Quoted(quote of the day)
20 FTP data
21 FTP control
23 Telnet
25 SMTP
37 Time
42 Host name server
49 Login host protocol
53 Domain name server
80 World wide web HTTP
102 ISO-TSAP
103 x.4000
104 X.4000 sending server
111 Sun RPC
139 NetBIOS session source
160-223 Reserved
Ghi chú :
Trị số của cổng trong các ứng dụng :
- Các ứng dụng chuẩn : 0 – 999
- Các ứng dụng không chuẩn : 1000 – 64000
II.1.2 GIAO THỨC IP
Internet protocol (IP) là một dạng giao thức truyền tin được thiết kế cho mạng
chuyển mạng gói. Vai trò của IP tương đương với vai trò của tầng mạng trong mô hình OSI.
Giao thức IP đảm nhiệm việc chuyển những gói mạch dữ liệu (datagram) từ địa chỉ nguồn
đến địa chỉ đích. Địa chỉ IP có địa chỉ cố định (4 byte) dùng để xác định duy nhất các trạm
làm việc đang tham gia vào việc truyền, nhận dữ liệu. Trong trường hợp mạng truyền tin chỉ
có thể truyền những gói dữ liệu có kích thức nhỏ, giao thức IP cũng đảm nhiệm chức năng
chia nhỏ các gói dữ liệu có kích thước lớn trước khi truyền và gộp chúng lại chúng sau khi
nhận được.[5]
Giao thức IP được thiết kế với chức năng chuyển 1 gói các bit, gọi là internet
datagram từ địa chỉ nguồn đến địa chỉ đích. Đây là một giao thức theo kiểu không liên kết,
nghĩa là không có giai đoạn thiết lập liên kết trước khi truyền dữ liệu, nó cũng không có các
cơ chế bảo đảm thông tin tới đích an toàn, không có cơ chế điều khiển luồng dữ liệu. Trên
thực tế việc thông báo về lỗi đường truyền có thể được thực hiện nhờ một giao thức khác có
tên ICMP( Internet Control Message Protocol).
Địa chỉ IP như đã nói ở trên, là một chuỗi bit có độ dài 4 byte, được phân chia làm 5
lớp và các bit đầu tiên được dùng làm định danh lớp địa chỉ.
II.2 GIAO THỨC TCP
Giao thức TCP là giao thức dùng cho tầng ngay trên tầng IP. Đối với mô hình OSI, ta
có thể thấy tầng TCP có vai trò tương ứng với các tầng giao vận và tầng phiên .
Khác với IP, TCP là giao thức có liên kết (connection oriented), nghĩa là nhất thiết
phải có giai đoạn thiết lập liên kết giữ các cặp thực thể TCP trước khi chúng có trao đổi dữ
liệu với nhau. Giao thức TCP cung cấp một khả năng truyền dữ liệu một cách an toàn giữa
các thực thể trên mạng. Nó cung cấp các chức năng nhằm kiểm tra tính chính xác của dữ
liệu khi gửi đến và gửi lại dữ liệu khi có lỗi xảy ra.[5]
Đơn vị dữ liệu cơ bản của TCP gọi là segment. Trong segment có một cặp tham số là
số hiệu cổng của trạm nguồn và số hiệu cổng của trạm đích. Mỗi một tiến trình ứng dụng tại
một trạm sẽ truy cập các dịch vụ TCP thông qua một cổng. Một cổng như vậy kết hợp với
một địa chỉ IP sẽ tạo thành một socket duy nhất trong mạng. Dịch vụ TCP được cung cấp
nhờ liên kết logic giữa một cặp socket, mỗi socket có thể tham gia liên kết với nhiều socket
ở xa khác nhau. Trước khi truyền dữ liệu chúng thiết lập liên kết và khi không có nhu cầu
truyền dữ liệu nữa thì liên kết sẽ được giải phòng.
Mỗi ứng dụng TCP gồm hai phần là client và server.
Lưu đồ sau minh họa các bước cần thiết để các ứng dụng client và server giao tiếp với nhau
:
Hình II.4 Tạo kết nối giữa client và server theo giao thức TCP
II.2.1 CÁCH THỨC CÀI ĐẶT ỨNG DỤNG TCP SERVER
Ứng dụng TCP server làm việc theo qui trình sau:
Gọi hàm socket để tạo một socket.
Gọi hàm bind để kết buộc socket với một port, đối với mỗi giao thức ứng dụng chuẩn
thì sẽ có một hằng số được định nghĩa sẵn trong winsock cho port của giao thức đó.
Gọi hàm listen để chờ đến khi có một client nối vào port.
Khi có một client nối vào thì hàm listen trả điều khiển về, ứng dụng server gọi hàm
accept để xác nhận mối nối của client.
Gọi các hàm gửi hay nhận dữ liệu để trao đổi thông tin với client, ví dụ send, recv.
Sau khi đã hoàn tất quá trình trao đổi dữ liệu, ứng dụng server gọi hàm closesocket
để đóng socket đã tạo.
Hình II.5 Sơ đồ giao tiếp giữa server với client
II.2.2 CÁCH THỨC CÀI ĐẶT ỨNG DỤNG CLIENT TCP
Ứng dụng client TCP làm việc theo qui trình sau :
- Gọi hàm socket để tạo một socket .
- Gọi hàm connect để nối vào server.
- Gọi hàm gửi hay nhận dữ liệu để trao đổi thông tin với server, ví dụ như hàm
send, recv.
- Sau khi đã hoàn tất quá trình trao đổi dữ liệu, ứng dụng client gọi hàm
closesocket để đóng socket đã tạo.
Hình II.6 Sơ đồ giao tiếp của client với server
Ghi chú:
Các bước trên cơ bản là giống nhau cho các ứng dụng client và server viết trên
Windows NT và UNIX. Tuy nhiên, với ứng dụng viết trên UNIX thì không cần gọi các hàm
WSAStartup, WSACleanup để khởi tạo thư viện quản lý socket và đóng bộ phận này. Lý
do, với UNIX thì bộ phận quản lý socket đã được hệ điều hành nạp sẵn.
II.3 GIAO THỨC UDP
Giao thức UDP cung cấp khả năng broadcast trên hệ thống mạng TCP/IP . Chúng ta
cũng cần cài đặt ứng dụng client và server. Ứng dụng client có nhiệm vụ gửi thông báo đến
tất cả các instance của ứng dụng server đang chạy trên hệ thống mạng.[5]
II.3.1 CÁCH CÀI ĐẶT ỨNG DỤNG SERVER UDP
Ứng dụng server UDP được cài đặt đơn giản hơn ứng dụng TCP. Chúng ta cần các
bước chính sau đây :
- Gọi hàm socket để tạo một socket .
- Gọi hàm bind để kết buộc socket với một port, đối với mỗi giao thức ứng dụng
chuẩn thì sẽ có một hằng số được định nghĩa sẵn trong winsock cho port của giao
thức đó.
- Gọi các hàm gửi hay nhận dữ liệu để trao đổi thông tin với client, ví như hàm
sendto, recvfrom.
- Sau khi đã hoàn tất quá trình trao đổi dữ liệu, ứng dụng server gọi hàm
closesocket để đóng socket đã tạo.
II.3.2 CÁCH CÀI ĐẶT ỨNG DỤNG CLIENT UDP
Ứng dụng client UDP thực hiện các bước sau :
- Gọi hàm socket để tạo một socket.
- Gọi hàm setsockopt để làm cho socket có khả năng broadcoast .
- Gọi các hàm gửi hay nhận dữ liệu để trao đổi thông tin với server, ví dụ hàm
sendto, recvfrom.
- Sau khi đã hoàn tất quá trình trao đổi dữ liệu, ứng dụng client gọi hàm
closesocket để đóng socket đã tạo.
Ghi chú:
Ngoài các bước trên, các ứng dụng viết trên Windows NT (kể cả client và server)
phải gọi hàm WSAStartup để khởi tạo thư viện liên kết động Windows Socket DLL và hàm
WSACleanup để đóng thư viện này. Tuy nhiên, đối với ứng dụng viết trên UNIX thì bộ
phận quản lý socket đã được hệ điều hành nạp sẵn và do đó không cần gọi các hàm như
WSAStartup, WSACleanup của Windows NT.
CHƯƠNG III
PHƯƠNG PHÁP MÃ HOÁ VÀ NÉN ÂM THANH
III.1 CÁC PHƯƠNG PHÁP MÃ HOÁ
III.1.1 GIỚI THIỆU CHUNG
Trong hệ thống xử lý âm thanh, âm thanh được mã hoá PCM. Các mã hoá này cho
phép khôi phục một cách tương đối trung thực tín hiệu âm thanh trong dải tần nghe được.
Tuy nhiên trong một ứng dụng đặc biệt như truyền dữ liệu âm thanh trên mạng, âm thanh
được truyền với tốc độ thấp hơn nhiều. Từ đó xuất hiện một số kỹ thuật mã hoá và nén tín
hiệu âm thanh như ADPCM, LPC, GSM….
Các loại phần mềm và phần cứng thực hiện mã hoá và nén âm thanh sang các loại dữ
liệu số thường được gọi là codec(Coder-Decoder). Có thể phân loại các phương pháp mã
hoá âm thanh thành 3 loại :
Mã hoá dạng sóng(waveform codec) : Giữ nguyên hình dạng nguyên thuỷ của sóng
âm. Phương pháp mã hoá này đòi hỏi tốc độ dữ liệu rất cao nhưng lại cho chất lượng âm
thanh rất tốt. Ưu điểm của bộ mã hoá này là độ phức tạp, giá thành thiết kế, độ trễ và công
suất tiêu thụ thấp. Bộ mã hoá sóng đơn giản nhất là điều biên xung mã (PCM)…Tuy nhiên
nhược điểm của bộ mã hoá là không tạo được âm thanh chất lượng cao tại tốc độ dưới 16
kbps, bộ mã nguồn khắc phục được nhược điểm này.[4]
Mã hoá nguồn (Source codec) : Cho phép đạt được tốc độ truyền dữ liệu thấp, có thể
thực hiện tại tốc độ bit > 2kbps, nhưng chất lượng âm thanh không cao.
Mã hoá hỗn hợp (hybrid codec) : Sử dụng cả hai phương pháp trên để tạo ra tín hiệu
âm thanh có chất lượng tốt ở tốc độ dữ liệu trung bình.
Hình III.1 Biểu đồ so sánh các phương pháp mã hoá
Rất tốt
Tốt
Trung
bình
Chất lượng tiếng nói
Mã hoá hỗn
hợp
Mã hoá dạng sóng
Mã hoá dự đoán
tuyến tính LPC
Tốc độ 2 4 8 16 32 64
Mã hoá dạng sóng
Đòi hỏi tốc độ bit rất cao nhưng tạo ra chất lượng âm thanh hoàn hảo và thời gian xử
lý rất ít, bởi vì phương pháp này chỉ đơn giản lấy mẫu tín hiệu ở tốc độ nào đó và lượng tử
hoá từng mẫu dùng lượng tử hoá tuyến tính. Cách tiếp cận này gọi là điều biến xung
mã(PCM). Am thanh mã hoá theo phương pháp PCM, lấy mẫu tốc độ 8KHz, lượng tử
8bit/mẫu, cho tốc độ bit ra là 64 kbps.
Phương pháp điều biên xung mã vi phân (DPCM) dự đoán giá trị hiện tại dựa vào các
giá trị trước của các mẫu quá khứ, chỉ lưu trữ giá trị sai số lượng tử. Giá trị sai số này sẽ nhỏ
hơn giá trị thực của mẫu, do đó cho phép lưu trữ giảm đi một phần.
Một cải tiến của DPCM là điều biến xung mã vi phân thích nghi(ADPCM). Bộ tiến
đoán và lượng tử hoá trong phương pháp này có khả năng thích nghi với tín hiệu cần mã
hoá. ADPCM hoạt động tốc độ 32 kbps cho chất lượng tương đối với PCM 64 kbps.
Mã hoá dự đoán tuyến tính LPC
Một vấn đề đáng quan tâm là phương pháp mã hoá dạng sóng là khi lấy mẫu tín hiệu
tương đương sẽ cho ra dòng dữ liệu khá lớn. Các phương pháp mã hoá nguồn đi theo một
cách tiếp cận khác, người ta phải qua tâm đến việc âm thanh được tạo ra như thế nào, nếu có
thể mô hình hoá cách tạo ra âm thanh thì không phải truyền đi các mẫu của tín hiệu âm
thanh mà chỉ cần gửi đi cách thức tạo ra âm đó trên đến bộ mã. Như vậy theo phương pháp
này thay vì mã hoá và truyền trực tiếp âm thanh thì người ta chỉ mã hoá các tham số tạo ra
âm thanh tại nơi nhận, từ các tham số này bộ tổng hợp tiếng nói sẽ tổng hợp nên tiếng nói
nhân tạo. Những phương pháp đi theo cách tiếp cận này gọi là Vocoder. Mô tả đơn giản
nhất của mô hình này là mã hoá dự đoán tuyến tính (LPC). Phương pháp mã hoá Vocoder
cho tốc độ bit rất thấp (>2 kbps) nhưng độ phức tạp cao hơn với phương pháp mã hoá dạng
sóng và chất lượng âm thanh không trung thực .
Mã hoá hỗn hợp
Để giải quyết vấn đề của phương pháp mã hoá dạng sóng và của phương pháp mã
hoá dự đoán, một nhóm các phương pháp mã hoá sử dụng phương pháp AbS (Analysis by
Synthesis) để tạo ra mô hình máy phát âm phức tạp hơn. Trong khi Vocoder sử dụng bộ lọc
tuyến tính kích thích bởi chuỗi xung tuần hào hay chuồi nhiễu trắng để tạo ra âm hữu thanh
hay vô thanh, AbS sử dụng một dãy các trạng thái được lưu trữ trong codebook. Khi xem
xét một khung âm thanh, AbS kiểm tra khung đó với rất nhiều tín hiệu kích thước rồi sẽ
chọn tín hiệu nào đưa ra kết quả gần đúng nhất với âm thanh ban đầu. Bộ giải mã chỉ cần
biết chỉ số của xung kích thước đó, sau đó tra codebook rồi tái tạo lại âm thanh. Một số các
tham số khác nhau như năng lượng của sự kích thích và giá trị chu kỳ cũng cần đến khi giải
mã. Các phương pháp mã đi theo cách này đòi hỏi quá trình tính toán phức tạp, có thể tất cả
các mục trong từ điển đều phải thử để đưa ra giá trị tốt nhất.
Hệ thống thông tin toàn cầu GMS : GSM là mộ chuẩn điện thoại được Viện Tiêu
Chuẩn Viễn Thông Châu Au ETSI để ra. Đầu vào của bộ nén GMS 06.10 bao gồm các
khung 160 mẫu các tín giệu PCM tuyến tính lấy mẫu tại tần số 8 kHz. Chu kỳ mỗi khung là
20 ms, đây là khoảng thời gian rất ngắn và trong khoảng thời gian này cho phép được coi tín
hiệu âm thanh ổn định. Độ trễ truyền dẫn thông tin được tính bằng tổng thời gian xử lý và
kích thước khung của thuật toán.
Bộ mã hoá thực hiện việc nén một khung tín hiệu đầu vào 160 mẫu (20 ms) vào một
khung 260 bit. Như vậy một giây nó sẽ thực hiện nén được 13.133 bit ( tương đương với
1625 byte). Do vậy để nén một megabyte tín hiệu chỉ cần một thời gian chưa đầy 10
phút.[4]
Trung tâm của quá trình xử lý tín hiệu là bộ lọc. Đầu ra của bộ lọc phụ thuộc rất
nhiều vào giá trị đầu vào đơn của nó. Khi có một dãy các giá trị đưa qua bộ lọc thì dãy tín
hiệu này sẽ được dùng để kích thích bộ lọc. Dạng của bộ nén GMS 06.10 dùng để nén tín
hiệu âm thanh bao gồm 2 bộ lọc và một giá trị kích thước ban đầu. Bộ lọc ngắn hạn dự đoán
tuyến tính được đặt tại tầng đầu tiên của quá trình nén và tại tầng cuối cùng trong suốt quá
trình dãn. Nó được kích thích bởi đầu ra của bộ lọc dự đoán dài hạn (LTP).
III.1.2 PHƯƠNG PHÁP ĐIỀU BIẾN XUNG MÃ PCM
Mã hoá tín hiệu âm thanh liên qua tới các bước sau đây :
a. Tín hiệu âm thanh được lấy mẫu với tần số tối thiểu là :
max2 ff s
fs : tần số lấy mẫu
fmax : tần số lớn nhất trong tín hiệu được lấy mẫu.
Việc lấy mẫu như vậy là đảm bảo hoàn toàn khôi phục lại được tín hiệu tương đương
ban đầu bởi thiết bị khôi phục thiết bị. Tần số lấy mẫu nhỏ nhất cũng có thể nhỏ hơn 2fmax
nếu tần số thấp nhất của tín hiệu khác không.
b. Biên độ của từng mẫu tín hiệu được lượng tử hoá thành một trong số 2B mức. Điều này
có nghĩa dùng B bit trên một mẫu và thông lượng là 2fsB bit/giây với tín hiệu được lọc
thông thấp.
c. Các mức biên độ riêng biệt được thể hiện bằng các từ nhị phân riêng biệt có chiều dài B.
ví dụ : với B =2 thì một từ có thể thể hiện cho 4 mức riêng biệt bằng cách sử dụng các từ
mã 00, 01, 10 và 11.
d. Đối với việc giải mã, các từu nhị phân được ứng thành các mức biên độ và chuỗi xung
biên độ – thời gian được lọc thông thấp với một bộ lọc có dải tần giới hạn là fs. Bây giờ
chúng ta giả sử là các bước a, c và d có thể thực hiện một cách hoàn toàn chính xác,
chúng ta tập trung vào việc xác định các lỗi lượng tử xuất hiện trong bước b.
Cho bước lượng tử (kí hiêu : ). Nếu số lượng các mức lượng tử rất lớn, người ta giả sử
rằng sai số lượng tử có phân bố đồng nhất như sau :
1
)(Ep , -
22
E (1)
Điều này sẽ không đúng nếu tín hiệu là bão hoà bộ lượng tư. Ví du như biểu diễn
hình 1, đầu ra của bộ lượng tử có thể bão hoà tại 5 với tần số đầu và vượt qua số lượng và
mỗi lượng tử trong trường hợp này sẽ là một hàm tăng tuyến tính.[4]
Hình III.2 a.Đặc tuyến lượng tử hoá;b.Đặc tuyết sai số lượng tử; c.VD lượng tử đều
Nếu như ta cho rằng bộ lượng tử không bị bão hoà. Theo công thức (1) thì giá trị
bình phương trung bình của sai số lượng tử là :
2
2
2
2
12
)( dEEpE (2)
Nếu giá trị độ rộng của đầu vào X là Xrms, thì tỷ số tín hiệu/nhiễu được tính bởi :
12// 22 rmsXSNR (3)
Ví dụ, cho bộ lượng tử có khoảng biên độ từ – 4Xrms đến + 4Xrms( để thuận tiện về
mặt lý thuyết, thì độ rộng 8Xrms của bộ lượng tử không có ràng buộc riêng, ngoại trừ các
trường hợp đặc biệt. Ví dụ, nếu tín hiệu PDF p(x) được biến đổi bằng hàm Gaussian trung
bình – không, thì các mẫu tín hiệu sẽ rơi bên ngoài khoảng 8Xrms của bộ lượng tử với xác
xuất nhỏ hơn 1/10.000). bước lượng tử đều có thể được thực hiện tỷ số của khoảng biên độ
trên số lượng các bước lượng tử (số các mức đầu ra).
BrmsX 2/8 (4)
Từ (3) và (4) ta có :
2,76log10)( 10 BSNRdBSNR (5)
Công thức trên miêu tả rất tốt về PCM với các điều kiện sau :
1.Hệ thống hoạt động với kênh sạch(không nhiễu), chỉ giới hạn về sai số lượng tử( và không
gây ra lỗi các từ mã khi kênh có nhiễu).
2.Tín hiệu đầu vào đủ phức tạp để loại trừ cấu trúc thời gian hiển nhiên trong dạng tín hiệu
lỗi, như vậy biểu diễn tĩnh của lỗi trong 1 được thể hiện đầy đủ.
3.lượng tử hoá đủ mịn(B>6) để ngăn chặn những thành phẩm liên quan đến tín hiệu trong
dạng tín hiệu lỗi, sai số có thể được đo trong biểu thức công suất nhiễu hay là biến sai số 2.
4.Bộ lượng tử được sắp với thanh biên độ từ (-4Xrms;+4Xrms)
như vậy trong lượng tử đều, cứ thêm 1 bit thì được lợi 6 dB và để có chất lượng thích hợp
thì B >11 do đó thông lượng tương đối lớn.
III.2 CÁC PHƯƠNG PHÁP NÉN TIẾNG NÓI
III.2.1 GIỚI THIỆU CHUNG
Y tưởng nén tiếng nói là để giảm kích thước nhằm giúp ít tốn băng thông truyền qua
mạng. Dòng dữ liệu tiếng nói được giải nén ở tốc độ lấy mẫu mặc định ( 8bits/mẫu, 8 khz,
kênh mono) sẽ yêu cầu đường truyền có tốc độ 8000 mẫu/giây * 8 bits/ mẫu = 64 Kbits/giây
để truyền dữ liệu qua mạng. Do đó, tùy theo tốc độ đường truyền thực tế trên mỗi mạng mà
chọn giải pháp nén hay không nén dữ liệu trước khi truyền dữ liệu âm thanh qua mạng, cũng
như chọn tỉ lệ nén là bao nhiêu cho phù hợp (chọn giải thuật nén). Vì nếu dữ liệu được nén
thì phải giải nén khi được truyền đến máy nhận, do đó cũng tốn thời gian để nén và giải nén
dữ liệu, điều này dẫn đến ảnh hưởng thời gian thực của hệ thống.
Đối với các mạng cục bộ, thường có tốc độ truyền của mạng cao nên có thể không
cần phải nén tiếng nói trước khi truyền.
Ngược lại, đối với mạng Internet, hệ thống được kết nối với Internet thông qua các
modem chuẩn có tốc độ thấp 14,4 Kbits/s hoặc 28,8Kbits/s thì nhất thiết phải nén tiếng nói
trước khi truyền và giải nén trước khi phát. Hai phương pháp nén âm thanh thường được
dùng nhất để giảm băng thông là GSM và ADPCM.[2]
III.2.2 CÁC PHƯƠNG PHÁP NÉN CỤ THỂ
III.2.2.1 Phương pháp nén tiếng nói theo chuẩn GSM
Phát triển tại Đại học Kỹ thuật Berlin vào năm 1992, GSM là một trong những
phương pháp nén âm thanh phức tạp nhất đang được sử dụng, cho tỉ lệ nén 1:10. Giải thuật
GSM dựa trên giao thức truyền thông Mobile Phone, hiện tại là giao thức phổ biến nhất tại
Châu Âu đối với điện thoại di động.
Đầu vào của GSM bao gồm các frames 160 tín hiệu, những tín hiệu PCM tuyến tính
13 bits lấy mẫu ở 8 Khz. GSM có sẵn trong thư viện C có thể được dùng để tạo ra một đối
tượng gsm giữ trạng thái cần thiết hoặc để mã hóa những mẫu PCM tuyến tính thành các
frames GSM, hoặc giải mã các frames GSM thành các frames PCM tuyến tính. Bộ mã hóa
nén 160 frames PCM 16 bits thành các frames GSM 260 bits. Tương ứng một giây tiếng nói
thành 1625 bytes. Bởi vì mẫu 260 bits không chẵn để gắn vào các bytes 8 bits, nên bộ mã
hóa sẽ mã hóa frame 160 bytes thành frame GSM 264 bits. Một buffer GSM nén 1 Mb có
thể lưu tiếng nói gần 10 phút.[2]
Một dòng dữ liệu tiếng nói giải nén 16 bits/mẫu ở 8Khz yêu cầu băng thông tốc độ
128 Kbits/s, trong khi đó băng thông để truyền qua mạng nếu dùng giải thuật nén GSM ,
tiếng nói 16 bits/mẫu chỉ cần:
( 264 bits * 8.000 mẫu/giây)/160 mẫu = 13,2 Kbits/giây
Cho tỉ lệ nén 128/13,2 = 9,7 tương đương 10 :1.
III.2.2.2 Phương pháp nén ADPCM
Nguyên tắc :
Là một phương pháp có thể được dùng để nén các khối dữ liệu tiếng nói trước khi
chúng được truyền đến các máy nhận và giải nén chúng để phát lại sau khi được nhận từ
đường truyền.
Hình sau là một giản đồ khối đơn giản của bộ mã hóa và giải mã ADPCM :
Hình III.3 Sơ đồ khối bộ mã hoá và giải mã ADPCM
Bộ mã hóa ADPCM giả sử rằng những mẫu âm thanh kế cận nhau sẽ giống nhau, vì
thế thay vì biểu diễn mỗi mẫu độc lập như PCM. ADPCM tính toán sự chênh lệch giữa các
mẫu âm thanh và giá trị tiên đoán của nó và tạo ra những giá trị vi phân PCM. Có nhiều giải
thuật ADPCM khác nhau được đề nghị. Trong phạm vi luận văn này em tìm hiểu và ứng
dụng giải thuật đưa ra bởi IMA (Interactive Multimedia Association) .
Giải thuật IMA ADPCM nén những mẫu PCM tuyến tính thành các mức lượng hóa 4
bits, trong đó mỗi mẫu DPCM được biểu diễn bằng các giá trị âm thanh 16 bits, do đó giải
thuật này cung cấp một tỉ lệ nén là 4:1. Ví dụ tiếng nói 16 bits/mẫu, 8 Khz, kênh mono yêu
cầu tốc độ truyền dữ liệu là 128 Kbits/s , khi nén với giải thuật IMA_ADPCM chỉ cần tốc
độ truyền dữ liệu 32 Kbits/s là đủ.
Quá trình thực hiện của giải thuật IMA_ADPCM là đọc từ những bộ đệm có giá trị
kiểu nguyên và nén chúng thành một mẫu âm thanh 16 bits được biểu diễn bằng các mức
lượng hóa 4 bit. Bởi vì không có giới hạn trong kích thước buffers tiếng nói nên những mã
ADPCM được kết hợp một cách dễ dàng với các phần còn lại của chương trình để nén tiếng
nói khi thu và giải nén trở lại khi phát.
Giải thuật nén IMA ADPCM :
Theo sơ đồ mã hóa trên chúng ta thấy giải thuật IMA ADPCM tính toán sự chênh
lệch giữa mẫu âm thanh hiện tại X[n] và mẫu đã tiên đoán trước Xp[n-1] và sử dụng sự
chênh lệch đó để tính toán mức lượng hóa D[n] cho mỗi mẫu. Giá trị Xp[n-1] chỉ là giá trị
trễ thời gian của giá trị X[n]. Hình sau chỉ ra sơ đồ khối của quá trình lượng hóa dùng giải
thuật IMA. Mỗi mức đầu ra của bộ lượng hóa được biểu diễn bằng bốn bits. Bit thứ tư là bit
dấu của mức độ lượng hóa.[2]
Hình III.4 Sơ đồ giải thuật mã hoá nén IMA ADPCM
Từ sơ đồ giải thuật ta thấy rằng bit thứ ba được cài đặt lên 1 nếu độ chênh lệch giữa
X[n] và Xp[n-1] là lớn hơn hay bằng step_size. Sau đó step_size được chia 2 và so sánh trở
lại với độ chênh lệch (độ chênh lệch sẽ được tính lại Mẫu = mẫu-kích thước mẫu tại bước
giải thuật nếu bit thứ ba được cài lên1). Bit thứ hai cũng được cài lên 1 khi độ chênh lệch
mới ≥ kích thước mẫu tại bước giải thuật/2. Bộ mã hóa một lần nữa laị chia kích thước mẫu
tại bước giải thuật cho 2 và so sánh với độ chênh lệch mới và thiết lập giá trị cho bit một
tương tự như các bước trước.
CHƯƠNG IV
TÌM HIỂU HỖ TRỢ CỦA WINDOWS SDK
TRONG XỬ LÝ VÀ TRUYỀN NHẬN ÂM THANH
IV.1 MÔI TRƯỜNG LẬP TRÌNH SDK ĐỐI VỚI TRUYỀN ÂM THANH
Môi trường Windows SDK là môi trường lập trình đa phương tiện dưới Windows,
cung cấp các hàm cấp thấp rất thích hợp cho các ứng dụng trên mạng. Một cách thức đơn
giản nhất trong việc xuất dữ liệu waveform ra loa là dùng hàm PlaySound. Chúng ta có thể
thao tác với dạng dữ liệu waveform bằng các hàm cấp thấp do hệ thống cung cấp. Ngoài ra
hệ thống còn cung cấp một cơ chế giúp người lập trình giao tiếp dễ dàng hơn với thiết bị, đó
là các hàm MCI.[6]
IV.1.1 CẤU TRÚC FILE WAVE VÀ HÀM PLAYSOUND
IV.1.1.1 Cấu trúc file âm thanh wave
Một file wave thật sự là một phần của một lớp file lớn hơn dùng bởi các hàm
multimedia của windows là các file RIFF ( Resource Interchange File Format). Một file Riff
bao gồm một hoặc nhiều chunk. Trong mỗi chunk có con trỏ chỉ đến chunk kế tiếp. Mỗi
chunk có một mô tả kiểu theo sau bởi một số dữ liệu. Một ứng dụng để đọc các file RIFF có
thể bước qua một số chunk, đọc các chunk cần quan tâm và bỏ qua các chunk không liên
quan. Chunk file RIFF luôn luôn bắt đầu bằng header sau:
Typedef struct {
FOURCC ckID;
DWORD cksize;
}CK;
Trong đó:
FOURCC là một vùng 4 bytes định nghĩa loại chunk. Vùng này sẽ chứa từ WAVE
đối với file wave.
ckSize đặc tả kích thước dữ liệu trong chunk, sau header này chúng ta sẽ tìm thấy
cSize bytes dữ liệu.
Các chunk có thể chứa các subchunks. Cấu trúc thật sự một file wave cơ bản bao
gồm một chunk fmt theo sau là một chunk dữ liệu. Có thể có những chunk khác phía sau
chunk WAVE nhưng thiết bị sử dụng file WAVE sẽ bỏ qua các chunk này. Hình sau mô tả
cấu trúc file RIFF chứa dữ liệu WAVE.
ID
SIZE
FROM TYPE
"fmt"
SIZE
"data"
SIZE
Hai subchunk trong chunk wave đặc tả thông tin về một âm thanh file wave và sau đó
là chính dữ liệu âm thanh. Chunk fmt chứa chủ yếu đối tượng WAVEFORMAT và một số
dữ liệu thêm vào gắn ở cuối chunk. Một đối tượng WAVEFORMAT được định nghĩa như
sau :
Typedef struct waveformar_tag{
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
} WAVEFORMAT;
Trong đo :
wFormatTag : Chứa hằng WAVE_FORMAT_PCM được định nghĩa trong
MMSYSTEM.H như sau:
# define WAVE_FORMAT_PCM 1
Giá trị WAVE_FORMAT_PCM báo cho phần mềm đọc file wave biết cách âm thanh trong
nó được mã hoá.
nChannels : Của đối tượng WAVEFORMAT có 2 giá trị :
1 đối với âm thanh mono.
2 đối với âm thanh stereo.
nSamplePerSec : Cho biết tần số lấy mẫu của âm thanh để có thể thu và phát cùng
một tốc độ, giá trị thông thường của field này nhận một những giá trị sau:
11025 - 11,025Khz
22055 - 22,050Khz
44100 - 44,1 Khz
nAvgBytesPerSec : Cho biết số bytes trung bình trong mỗi giây để thu và phát dữ
liệu wave.
nBlockAlign : Xác định số bytes yêu cầu chứa trong một mẫu
Những mẫu có độ phân giải nhỏ hơn hoặc bằng 8 bits có thể lưu vào 1 bytes.
Những mẫu có độ phân giải từ 9 đến 16 bits yêu cầu 2 bytes.
Những mẫu stereo yêu cầu số bytes gấp đôi những mono.
Trong cấu trúc trên không định nghĩa số bits thật sự trong một mẫu dữ liệu âm thanh
file wave, để định nghĩa số bits trong một mẫu ta dùng cấu trúc sau:
Typedef struct pvmwaveformat_tag{
WAVEFORMAT wf;
WORD wBitsPerSample;
} PCMWAVEFORMAT;
Trong đó:
wf: Đối với dữ liệu subchunk fmt của một chunk WAVE chúng ta thật sự làm việc
với đối tượng PCMWAVEFORMAT.
nBitsPerSample: Xác định số bits thật sự trong một mẫu .
Trong một mẫu mono 8 bits dữ liệu của chunk dữ liệu gồm một chuỗi dài có giá trị 1 byte.
Những mẫu stereo được chia ra với byte đầu dùng cho kênh bên trái và byte thứ hai dùng
cho kênh bên phải, như vậy mỗi mẫu stereo 8 bits sẽ cần 2 bytes.[6]
Để làm việc với một file RIFF bao gồm các bước sau :
Mở file.
Vào chunk cần thiết.
Chuyển con trỏ file vào vị trí bắt đầu dữ liệu của chunk.
Hoàn tất, ra khỏi chunk.
Vào chunk kế tiếp.
IV.1.1.2 Hàm Playsound
Chúng ta dùng hàm PlaySound để play dữ liệu dạng waveform hoặc chúng ta có thể
dùng hàm sndPlaySound. Tuy nhiên trong môi trường Win32 thì nên dùng hàm PlaySound.
Hàm PlaySound cho phép chúng ta chỉ định các thông số nguồn âm thanh theo các
cách sau:
Dùng tên alias khai báo trong file WIN.INI
Dùng tên file.
Dùng chỉ số nhận dạng tài nguyên
Waveform-Audio Files
Trong môi trường Windows, phần lớn các file âm thanh dạng waveform đều có phần mở
rông là .WAV
Ví dụ dưới đây minh họa cho việc phát file âm thanh “AmThanh.WAV”
PlaySound("C:\\SOUNDS\\AmThanh.WAV", NULL, SND_SYNC);
Play sound theo các hiện tượng
Hàm PlaySound còn cho phép chúng ta xuất âm thanh tùy theo một sự kiện nào đó
xảy ra trong hệ thống như click mouse hay nhấn một phím nào đó. Hệ thống sẽ phát âm
thanh tùy theo hiệc tượng xảy ra để cảnh báo người sử dụng. Am thanh dạng này được gọi
là sound events.
Để xác định sound event, hàm PlaySound sẽ được gọi với thông số pszSound trỏ đến
bảng đăng ký sự kiện. Ví dụ chúng ta sẽ gọi hàm PlaySound ứng với sự kiện mouse click
như sau:
PlaySound("MouseClick", NULL, SND_SYNC);
IV.1.2 GIAO TIẾP AUDIO VỚI BẰNG CÁC DỊCH VỤ CẤP THẤP
Trong phần này chúng ta sẽ khảo sát việc giao tiếp với thiết bị audio bằng các hàm
cấp thấp, các hàm này phù hợp với các ứng dụng cần giao tiếp. Các hàm và cấu trúc cấp
thấp này đều có prefix là wave.[6]
Thiết bị và dữ liệu
Khi muốn giao tiếp với thiết bị, chúng ta phải mở thiết bị để sử dụng và sau khi sử
dụng xong thì phải đóng thiết bị lại. Trong khi sử dụng chúng ta sẽ truy xuất các tính năng
của thiết bị và theo dõi thiết bị thực thi bằng các handles và Identifiers.
IV.1.3 SỬ DỤNG CÁC THIẾT BỊ XUẤT NHẬP, HỖ TRỢ WAVEFORM AUDIO
Chúng ta dùng hàm waveOutOpen để mở thiết bị đầu ra nhằm xuất dữ liệu dạng
waveform. Hàm sẽ mở thiết bị waveOut và trả handle về cho ứng dụng. Hệ thống
multimedia sẽ hỗ trợ nhiều dạng output dữ liệu khác nhau do đó khi mở thiết bị nếu cần
chúng ta phải chỉ định rõ thông số dữ liệu. Ví dụ chúng ta dùng cờ WAVE_MAPPER để
xác định thiết bị output sẽ xuất âm thanh dạng waveform.
Dò tìm thiết bị
Windows cung cấp các hàm sau giúp chúng ta xác định các thiết bị trong hệ thống
phù hợp cho yêu cầu của mình.
Hàm Chức năng
AuxGetNumDevs Truy xuất số lượng các thiết bị sẵn có trong
hệ thống
WaveInGetNumDevs Truy xuất số lượng các thiết bị nhập hỗ trợ
waveform audio sẵn có trong hệ thống
WaveOutGetNumDevs Truy xuất số lượng các thiết bị xuất hỗ trợ
waveform audio sẵn có trong hệ thống
Các thiết bị khai báo trong hệ thống được đánh các chỉ số nhận dạng bắt đầu từ 0.
Sau khi xác định số lượng các thiết bị sẵn có trong hệ thống, chúng ta có thể dò tìm khả
năng của từng thiết bị bằng các hàm sau:
Hàm Chức năng
AuxGetDevCaps Truy xuất khả năng của thiết bị xuất
WaveInGetDevCaps Truy xuất khả năng của thiết bị nhập dạng
waveform
WaveOutGetDevCaps Truy xuất khả năng của thiết bị xuất dạng
waveform
Các hàm truy xuất này sẽ truy xuất này sẽ lấy các cấu trúc liên quan đến khả năng
của thiết bị. Các cấu trúc dưới đây sẽ tương ứng với các hàm liệt kê ở trên:
Hàm Structure
AuxGetDevCaps AUXCAPS
WaveInGetDevCaps WAVEINCAPS
WaveOutGetDevCaps WAVEOUTCAPS
Thiết bị waveform có khả năng hỗ trợ các dạng format không chuẩn. Dạng format
không chuẩn này được dùng trong cấu trúc WAVEFORMATEX.
Handle và Identifier của thiết bị
Khi mở thiết bị, chúng ta sẽ nhận được handle hay thông số Identify của thiết bị.
Chúng ta sẽ điều khiển thiết bị qua các thông số này.
Sự khác biệt giữa handle và identify là khó thấy nhưng rất quan trọng.
Identitier của thiết bị được chỉ định ngầm định từ số lượng các thiết bị sẵn có trong
hệ thống. Identifier sẽ được trả về khi chúng ta dùng các hàm auxGetNumDevs,
waveInGetNumDevs, or waveOutGetNumDevs Hàm.
Handle của thiết bị là thông số trả về của device driver khi chúng ta dùng các hàm
waveInOpen hay waveOutOpen.
Waveform-Audio Output Data Types
Các thông số dưới đây được dùng cho thiết bị waveform output
Cấu trúc Chức năng
HWAVEOUT Handle của thiết bị waveform output
WAVEFORMATEX Cấu trúc dùng chỉ định dạng format thiết bị
output/ input
WAVEHDR Cấu trúc dùng làm header cho khối dữ liệu
waveform. Cấu trúc được dùng cho cả thiết bị
output và input
WAVEOUTCAPS Cấu trúc dò hỏi khả năng của thiết bị output.
Chỉ định dạng format của dữ liệu waveform audio
Khi chúng ta mở thiết bị output bằng hàm waveOutOpen, thông số pwfx sẽ chỉ định
cấu trúc WAVEFORMATEX xác định dạng format của dữ liệu waveform. Đây là cấu trúc
mở rông của cấu trúc WAVEFORMAT.
Ghi dữ liệu waveform
Sau khi mở thiết bị xuất, chúng ta có thể xuất dữ liệu bằng cách gọi hàm
waveOutWrite. Hàm sẽ gửi khối dữ liệu âm thanh ra thiết bị xuất. Chúng ta dùng cấu trúc
WAVEHDR để chỉ định header của khối dữ liệu được gửi ra. Header này gồm con trỏ tới
khối dữ liệu đã lock, chiều dài khối dữ liệu và một số thông số cờ. Khối dữ liệu phải được
prepare trước khi đem ra sử dụng.
Sau khi gửi khối dữ liệu đến thiết bị output, chúng ta phải chờ driver hoàn tất việc xử
lý khối dữ liệu trước khi giải phóng nó. Khi chúng ta cần gửi nhiều khối dữ liệu liên tục,
chúng ta sẽ phải theo dõi việc xử lý hoàn tất khối dữ liệu để có thể gửi khối tiếp theo.
PCM Waveform-Audio Data Format
Thông số lpData trong cấu trúc WAVEHDR sẽ trỏ đến dữ liệu đã được lấy mẫu. Đối
với dữ liệu PCM 8-bit, mỗi giá trị lấy mẫu được biểu diễn bằng một số 8 bit không dấu. Đối
với dữ liệu PCM 16-bit, mỗi giá trị lấy mẫu được biểu diễn bằng một số 16 bit không dấu.
Bảng dưới đây cho chúng ta thấy các giá trị cao nhất, thấp nhất cũng như giá trị trung bình
của dữ liệu PCM:
Data format Maximum value Minimum value Midpoint value
8-bit PCM 255(0xFF) 0 128 (0x80)
16-bit PCM 32,767 (0x7FFF) -32,768 (0x8000) 0
Các gói dữ liệu PCM
Thứ tự của dữ liệu thay đổi tùy theo dạng format 8-bit hay 16-bit, stereo hay mono.
Bảng dưới đây trình bày các gói dữ liệu của các dạng PCM khác nhau:
PCM waveform Chức năng
8-bit mono Mỗi giá trị lấy mẫu là 1 byte tương ứng một kênh. Các
giá trị mẫu xếp theo thứ tự 1, 2, 3, 4 . . .
8-bit stereo Mỗi giá trị lấy mẫu là 2 byte. Các giá trị mẫu xếp theo
thứ tự 1, 2, 3, 4 . . . Với mỗi giá trị mẫu byte đầu là
kênh 0 (trái) còn byte sau là kênh 1 (phải).
16-bit mono Mỗi giá trị lấy mẫu là 2 byte. Các giá trị mẫu xếp theo
thứ tự 1, 2, 3, 4 . . . Với mỗi giá trị mẫu byte đầu là
byte thấp của kênh 0 còn byte sau là byte cao của kênh
0.
16-bit stereo Mỗi giá trị lấy mẫu là 4 byte. Các giá trị mẫu xếp theo
thứ tự 1, 2, 3, 4 . . . Với mỗi giá trị mẫu byte đầu là
byte thấp của kênh 0 (trái), byte thứ 2 là byte cao của
kênh 0, byte thứ 3 là byte thấp của kênh 1 (phải) còn
byte thứ 4 là byte cao của kênh 1.
Đóng thiết bị waveform-Audio Output
Sau khi thực thi công việc xong, chúng ta sẽ gọi hàm waveOutClose để đóng thiết bị.
Khi thiết bị đang thực thi mà gọi hàm này thì lỗi sẽ xảy ra. Nếu chúng ta muốn đóng thiết bị
giữa chừng thì đầu tiên chúng ta nên gọi hàm waveOutReset trước khi đóng thiết bị. Nhưng
trước đó cũng cần gọi hàm waveOutUnprepareHeader để unprepare tất cả các khối dữ liệu.
Playing Waveform-Audio Files
Chúng ta có thể dùng các hàm sau đây để xuất dữ liệu dạng âm thanh ra loa:
Hàm Chức năng
MessageBeep Xuất âm thanh dưới dạng thông báo của hệ thống
SndPlaySound Xuất âm thanh dưới dạng đăng ký trước trong hệ thống
hay là nội dung của một file wave
PlaySound
Giống như hàm trên và thêm cơ chế truy xuất trực tiếp tài
nguyên.
Các hàm PlaySound và sndPlaySound sẽ nạp hoàn toàn nội dung file wave vào bộ nhớ và
xuất ra ngõ output. Khả năng bộ nhớ của chúng có giới hạn nên chúng chỉ quản lý được các
nội dung dưới 100KB. Khi làm việc với các file có nội dung lớn hơn thì chúng ta có thể sử
dụng các dịch vụ do MCI cung cấp.
Sử dụng Windows message trong việc quản lý khi playback
Các Thông báo dưới đây có thể được sử dụng trong quá trình xuất dữ liệu:
Thông báo Chức năng
MM_WOM_CLOSE Được gửi đi khi đóng thiết bị bằng hàm
waveOutClose
MM_WOM_DONE Được gửi đi sau khi driver hoàn tất việc xuất dữ liệu
bằng hàm wafveOutWrite.
MM_WOM_OPEN Được gửi đi khi thiết bị được mở bằng hàm
waveOutOpen.
Các thông số wParam và lParam cũng rất cần thiết. Thông số wParam luôn luôn xác
định handle của thiết bị waveform-audio. Đối với thông số lParam, Thông báo
MM_WOM_DONE dùng thông số này trỏ tới cấu trúc WAVEHDR chỉ định sụ hoàn tất của
dữ liệu trong khi thông số này không được hai Thông báo còn lại dùng. Việc dùng này rất
hữu hiệu, Thông báo MM_WOM_DONE sẽ là tín hiệu được báo về sau khi việc playback
khối dữ liệu hoàn tất. Ta sẽ tiếp nhận Thông báo này và giải phóng các biến có liên quan.
Thay đổi volume của quá trình playback dữ liệu waveform audio
Chúng ta sẽ dùngcác hàm sau đây để lấy thông số volume cũng như thiết lập các
thông số này theo yêu cầu.
Hàm Chức năng
WaveOutGetVolume Truy xuất mức volume của thiết bị xuất
WaveOutSetVolume Thiết lập mức volume cho thiết bị
Giá trị volume là một số doubleword. Khi audio format là stereo, 16 bit cao chỉ giá trị
volume của channel phải và 16 bits thấp chỉ giá trị volume của channel trái. Còn nếu ở các
thiết bị không hỗ trợ 2 kênh thì 16 bit thấp sẽ được sử dụng chỉ giá trị volume còn 16 bit cao
sẽ không dùng đến.
Giá trị volume thay đổi từ giá trị 0x0 (silence) cho đến mức 0xFFFF (maximum)
Recording Waveform Audio
Chúng ta có thể sử dụng dịch vụ thu âm thanh theo chuẩn MCI. Tuy nhiên nếu thấy
cần thiết, chúng ta có thể sử dụng các hàm thu âm thanh cấp thấp.
Các thông số dữ liệu dưới đây sẽ đặc trưng cho dạng dữ liệu waveform audio input
Structure Chức năng
HWAVEIN Handle của thiết bị input
WAVEFORMATEX Cấu trúc của dạng dữ liệu được thiết bị input hỗ trợ
WAVEHDR Cấu trúc dùng làm header của khối dữ liệu input.
Nó cũng được dùng khi xuất dữ liệu ra Output
WAVEINCAPS Cấu trúc dùng dò hỏi các khả năng của thiết bị
input
Trước khi bắt đầu công việc thu dữ liệu, chúng ta phải dùng hàm waveInGetDevCaps
để dò hỏi khả năng cũng như xác định các thuôc tính của thiết bị. Hàm sẽ trả về cấu trúc
WAVEINCAPS xác định các thông số mong muốn.
Opening Waveform-Audio Input Devices
Để thu dữ liệu, trước hết chúng ta dùng hàm waveInOpen để mở thiết bị waveform
input. Nếu thực thi thành công, hàm sẽ trả về cho chúng ta handle của thiết bị.
Managing Waveform-Audio Recording
Sau khi mở thiết bị, chúng ta có thể tiến hành việc thu dữ liệu. Dạng dữ liệu
waveform thu được sẽ được đưa vào buffer, buffer này được trỏ đến trong cấu trúc
WAVEHDR. Trước khi được sử dụng, chúng ta phải prepare buffer này.
Windows cung cấp cho chúng ta các hàm sau đây dùng thu dữ liệu waveform:
Hàm Chức năng
WaveInAddBuffer Gửi một buffer cho device driver, thiết bị sẽ
thu dữ liệu vào khối này
WaveInReset Ngừng thu dữ liệu và đánh dấu tất cả các
buffer đã thu xong
WaveInStart Bắt đầu thu dữ liệu.
WaveInStop Kết thúc việc thu dữ liệu
Chúng ta dùng hàm waveInAddBuffer để gửi các khối buffer tới device driver. Khi
dữ liệu được điền đầy vào buffer, ứng dụng sẽ được thông báo bằng window Thông báo,
callback Thông báo, thread Thông báo, hay event, tùy theo cờ thông báo được chỉ định
trong hàm open device.
Trước khi bắt đầu thu dữ liệu, chúng ta phải gửi ít nhất một buffer dữ liệu cho thiết bị
input và khi đóng thiết bị, chúng ta gọi hàm waveInReset để đánh dấu các buffer đã được
thu xong.
Using Window Messages to Manage Waveform-Audio Recording
Các Thông báo sau đây sẽ được dùng để quản lý việc thu dữ liệu dạng waveform
audio:
Thông báo Chức năng
MM_WIM_CLOSE Được gửi đi khi thiết bị đóng lại khi gọi hàm
waveInClose
MM_WIM_DATA Được gửi đi khi thiết bị thu đầy một buffer khi gọi
hàm waveInAddBuffer
MM_WIM_OPEN Được gửi đi khi thiết bị được open khi gọi hàm
waveInOpen
Thông số lParam của MM_WIM_DATA là pointer trỏ đến cấu trúc WAVEHDR để
nhận dạng buffer dữ liệu. Buffer có thể không chứa đầy dữ liệu vì việc thu dữ liệu có thể kết
thúc trước khi buffer được thu đầy. Chúng ta có thể biết được kích thước thật sự của dữ liệu
bằng thông số dwBytesRecorded.
Audio data block
Hàm waveInAddBuffer và waveOutWrite có thông số yêu cầu ứng dụng chỉ định
khối dữ liệu cần cho thiết bị sử dụng cho việc thu hay playback. Các hàm trên sử dụng cấu
trúc WAVEHDR để miêu tả khối dữ liệu trên.
Trước khi sử dụng các hàm trên để gửi khối dữ liệu cho thiết bị. Chúng ta phải cấp
phát vùng nhớ cho khối dự liệu và khối header. Khối header phải được prepare và unprepare
bằng các hàm sau:
Hàm Chức năng
WaveInPrepareHeader Prepare khối dữ liệu input
WaveInUnprepareHeader Unprepare khối dữ liệu input
WaveOutPrepareHeader Prepare khối dữ liệu output
WaveOutUnprepareHeader Unprepare khối dữ liệu output
Trước khi gửi khối dữ liệu cho driver input hay output, chúng ta phải prepare chúng.
Sau khi thiết bị sử dụng xong, các khối dữ liệu phải được unprepare trước khi giải phóng
các vùng nhớ đã cung cấp.
Khi kích thước dữ liệu lớn, chúng ta phải cung cấp các buffer liên tục cho thiết bị,
quá trình này phải diễn ra liên tục cho đến khi hoàn tất công việc và thiết bị được đóng lại.
Ứng dụng phải xác định và quản lý thời điểm mà thiết bị hoàn tất việc thự thi trên
các khối dữ liệu để đưa ra các tác động thích hợp. Các cách sau đây được đưa ra:
Chi định hàm callback nhận Thông báo mà thiết bị gửi khi nó hoàn tất một khối dữ
liệu.
Sử dụng các event callback
Chỉ định window hay thread nhận Thông báo gửi từ thiết bị.
Xác định bit WHDR_DONE trong cờ dwFlags của cấu trúc WAVEHDR đi kèm với mỗi
khối dữ liệu.
Khi ứng dụng không đáp ứng được tốc độ xử lý các buffer thì chiến lược buffer kép
có thể được đưa ra để tăng tốc độ thực thi.
Chúng ta sẽ khảo sát một số phương thức xử lý sau khi thiết bị hoàn tất một khối dữ
liệu.
Dùng hàm callback để xử lý các driver messages
Để chỉ định hàm callback xử lý ứng vớicác driver message, chúng tachỉ định cờ
CALLBACK_FUNCTION trong biến fdwOpen và địa chỉ hàm xử lý trong biến dwCallback
khi gọi hàm waveInOpen hay waveOutOpen.
Messages gửi cho hàm callback tương tự như Thông báo gửi cho window, ngoại trừ
việc nó có hai thông số DWORD thay vì một thông số DWORD và một thông số UINT.
Để gửi dữ liệu cho hàm callback chúng ta có thể dùng một trong hai cách sau:
Dùng thông số dwInstance trong hàm open device
Dùng field dwUser trong cấu trúc WAVEHDR để chỉ định khối dữ liệu gửi cho device
driver.
Dùng event callback xử lý các driver message
Để dung event callback, chúng ta dùng hàm CreateEventđể truy xuất handle của
event. Trong hàm open thiết bị, chỉ định cờ CALLBACK_EVENT cho thông số fdwOpen.
Sau khi gọi hàm waveOutPrepareHeader nhưng trước khi gửi dữ liệu cho thiết bị, chúng ta
tạo ra một nonsignal event bằng cách gọi hàm ResetEvent, chỉ định event handle được lấy
từ hàm CreateEvent. Trong vòng loop để kiểm tra khi bit WHDR_DONE được set trong cấu
trúc WAVEHDR, chúng ta gọi hàm WaitForSingleObject, chỉ định thông số event handle
và giá trị time-out là INFINITE. Giá trị event callback là giá trị dùng gọi hàm callback.
Bởi vì event callback không xác định được thông báo xác định close, done hay open.
Ứng dụng phải kiểm tra tình trạng của hệ thống đang chờ sự kiện gì xảy ra để đưa ra các đáp
ứng chính xác.
Dùng window hay thread để xử lý các message driver
Để dùng hàm window callback, chúng ta chỉ định thông số CALLBACK_ WINDOW
trong biến fdwOpen và chỉ định handle của window trong thông số dwCallback khi gọi hàm
open thiết bị. Driver message sẽ được gửi tới window procedure.
Tương tự như vậy, chúngta sẽchỉ định thông số CALLBACK_THREAD và thread
handle trong hàm open khi chúng ta muốn thread xử lý các driver message.
Ngoài cách thức dùng hàm callback, chúng ta có thể dựa vào thông số dwFlags trong
WAVEHDR để xác định xem thiết bị có hoàn tất việc xử lý khối dữ liệu hay chưa.
Các hàm kiểm tra lỗi
Các hàm waveform audio sẽ trả về giá trị khác 0 khi có lỗi xảy ra. Windows cung cấp cho
chúng ta các hàm xác định lỗi dựa trên các thông số này. Ứng dụng sẽ dựa vào các thông số
xác định lỗi để quyết định công việc thực thi tiếp tục. Các hàm sau được dùng để xác định
các lỗi xảy ra:
Hàm Chức năng
WaveInGetErrorText Trả về chuỗi text xác định lỗi xảy ra của
input device
WaveOutGetErrorText Trả về chuỗi text xác định lỗi xảy ra của
output device
IV.2 KỸ THUẬT TRUYỀN NHẬN ÂM THANH TRÊN MẠNG IP
IV.2.1 MÔ HÌNH LIÊN KẾT VÀ TRAO ĐỔI DỮ LIỆU
Chương trình dùng giao thức TCP/IP làm giao thức giao tiếp. Việc thiết lập liên kết
cũng như trao đổi dữ liệu đều tuân theo các cấp của giao thức này. Việc gọi và thiết lập liên
kết được thực hiện theo mô hình client/server, việc trao đổi dữ liệu được thực hiện thông
qua socket theo giao thức TCP.
Có hai ý tưởng được đưa ra trong việc dùng socket để trao đổi dữ liệu.
Dùng 1 socket :
Mỗi máy dùng một socket để truyền nhận dữ liệu. Theo giao thức TCP sau khi hai
socket connect được với nhau thì việc tiến hành trao đổi dữ liệu sẽ bắt đầu. Chúng ta sẽ
dùng cặp socket này. Như vậy, một socket trên một máy đồng thời đảm nhận việc truyền dữ
liệu đi cũng như nhận dữ liệu về.[3]
socket socket
Hình IV.1 Mô hình dùng 1 socket
Cách dùng này có đặc điểm là việc tạo liên kết đơn giản, quá trình tạo liên kết hoàn
toàn giống như các bước trong việc tạo liên kết giữa các socket dùng giao thức TCP.
Chương trình chạy và lắng nghe ở một port xác định. Khi có một yêu cầu gọi liên kết đến,
chương trình sẽ tạo ra một socket để nối kết với socket gọi. Sau khi thiết lập liên kết thì các
socket bắt đầu gửi nhận dữ liệu. Socket sẽ gửi dữ liệu âm thanh đi đồng thời nhận dữ liệu
truyền tới và chuyển cho hệ thống xử lý.
Socket làm việc theo cách này sẽ nhận hai thông báo cùng một lúc. Khi có dữ liệu từ
mạng truyền tới, hệ thống sẽ thông báo cho socket để tiến hành việc nhận dữ liệu. Cũng
tương tự như vậy, khi có dữ liệu âm thanh sẵn sàng, hệ thống cũng sẽ gọi socket để truyền
đi.
Như vậy, khi thực thi socket sẽ nhận được hai thông báo của hệ thống. Vì việc truyền
nhận dữ liệu âm thanh là dạng dữ liệu liên tục cho nên tần suất mà hệ thống thông báo cho
socket là rất thường xuyên. Vì vậy, socket trong cùng một lúc có thể nhận được cả hai yêu
cầu truyền dữ liệu đi và nhận dữ liệu về. Thêm vào đó các hoạt động truyền nhận dữ liệu là
các hoạt động bị tắc nghẽn. Do đó chúng ta phải lưu ý đến hiện tượng này, socket có thể đáp
ứng không kịp nhu cầu của hệ thống.
Yêu cầu truyền
dữ liệu
Yêu cầu nhận
dữ liệu
socket
Chúng ta lấy một trường hợp ví dụ. Khi socket nhận được yêu cầu truyền dữ liệu đi,
nó sẽ lấy dữ liệu từ các buffer và truyền đi. Do quá trình truyền dữ liệu có thể bị tắc nghẽn,
socket sẽ phải chờ. Đồng thời trong lúc này, nó lại nhận được tín hiệu thông báo có buffer
kế tiếp cần truyền đi và tín hiệu thông báo có dữ liệu trên mạng truyền về. Với các yêu cầu
dồn dập như vậy, hệ thống có thể sẽ đáp ứng không kịp và chương trình có thể bị treo.
Vì vậy, khi dùng một socket để truyền nhận dữ liệu, chúng ta phải tính toán cân đối
thời gian giữa việc truyền dữ liệu đi và việc nhận dữ liệu về sao cho hợp lý để hệ thống có
thể làm việc liên tục được. Chúng ta có thể qui định thời gian cho việc truyền nhận. Trong
một thời điểm socket có thể chỉ làm việc truyền dữ liệu đi, các yêu cầu nhận dữ liệu sẽ bị
ngưng lại. Sau đó socket sẽ chỉ xử lý các yêu cầu nhận dữ liệu. Chiến lược này giúp giảm
nhẹ hoạt động của socket. Tuy nhiên, chúng ta cần áp dụng cho cả hai socket liên kết. Trong
một thời điểm, một socket sẽ truyền còn socket còn lại sẽ nhận dữ liệu, và thời điểm sau thì
quá trình sẽ diễn ra theo chiều ngược lại.
Dùng 2 socket :
Xuất phát từ ý tưởng trên, chúng ta có thể dùng hai socket trong việc trao đổi dữ liệu.
Một liên kết hình thành giữa hai máy sẽ gồm hai cặp socket liên kết với nhau. Một socket
chỉ đảm nhận việc truyền dữ liệu trong khi socket còn lại đảm nhận việc nhận dữ liệu.[3]
Socket
truyền
Socket
truyền
Socket
nhận
Socket
nhận
Yêu cầu truyền
dữ liệu Socket truyền
Hình IV.2 Mô hình dùng 2 socket
Vì mỗi socket chỉ nhận một tín hiệu nhất định. Socket truyền sẽ chỉ chú ý tới tín hiệu
báo có dữ liệu của hệ thống để tiến hành truyền dữ liệu đi. Trong khi đó, socket nhận sẽ chỉ
lưu ý đến tín hiệu báo có dữ liệu của hệ thống. Hai socket sẽ hoạt động độc lập với nhau và
công việc của một socket sẽ nhẹ nhàng hơn mô hình trên.
Tuy nhiên, trong mô hình này, việc thiết lập liên kết giữa hai máy sẽ trở nên phức tạp
hơn. Theo mô hình client/server, khi một socket gọi và thiết lập liên kết với chương trình ở
máy remote xong thì máy remote cũng phải tạo ra một socket và tiến hành liên kết ngược
lại. Sau khi cặp socket hoàn toàn liên kết xong thì hai máy mới coi như đã connect và tiến
hành truyền nhận dữ liệu.
Một khía cạnh khác cần lưu ý là tuy hai socket hoạt động độc lập với nhau nhưng
chúng đều thuộc cùng một chương trình và chúng đều tiến hành việc gửi nhận dựa trên các
giao thức lớp dưới chung. Do đó, trong một thời điểm chỉ có một hoạt động diễn ra hoặc là
truyền dữ liệu hoặc là nhận dữ liệu. Vì vậy thật ra hai socket cũng phải hoạt động phụ thuộc
nhau. Socket gửi dữ liệu phải chờ socket nhận nhận xong dữ liệu rồi mới bắt đầu truyền đi
và ngược lại việc truyền dữ liệu phải được hoàn tất thì việc nhận dữ liệu mới có thể tiến
hành được.
Một vấn đề khác nẩy sinh do đặc điểm của dữ liệu. Dữ liệu tiếp nhận là dạng dữ liệu
liên tục do đó, các tín hiệu mà hệ thống báo cho hai socket cũng xảy ra liên tục, vì vậy thực
sự rằng tuy chỉ làm một công việc nhưng khối lượng công việc mà socket phải đảm nhận là
rất lớn. Thêm vào đó, hai socket đều phụ thuộc vào một process do đó thật sự xét về mặt
thực thi của quá trình thì khả năng giảm nhẹ công việc là không bao nhiêu. Và khả năng hệ
thống bị treo do quá tải cũng vẫn có thể xảy ra.
Chúng ta có những cách giải quyết để giảm nhẹ việc thực thi của chương trình như
dùng cơ chế xử lý song song (thread) hay dùng cơ chế phân chia thời gian cho các hoạt
động như đã nói ở trên.
IV.2.2 CƠ CHẾ GỌI VÀ XÁC LẬP LIÊN KẾT
Khi liên kết được xác lập, chúng ta sẽ bắt đầu tiến hành trao đổi dữ liệu. Tuy nhiên
trước hết chúng ta cần khảo sát phương pháp gọi cũng như thiết lập liên kết. Chương trình
được hiện thực dựa trên cơ chế client/server cho nên việc tạo liên kết cũng dựa trên cơ chế
này. Ý tưởng chính là: khi chương trình bắt đầu thực thi, nó cũng bắt đầu lắng nghe lời gọi
liên kết ở một port xác định. Thực sự, trong chương trình chúng ta sẽ tạo ra một socket
server và lắng nghe ở một port qui ước trước. Khi một socket khác muốn tạo liên kết, nó sẽ
tiến hành gọi liên kết với socket server ở giá trị port này.[3]
Trong giao thức TCP/IP, một quá trình giao tiếp thông qua môi trường mạng phải có
một chỉ số port xác định. Các quá trình khác nhau phải có port khác nhau. Khi thiết kế mô
hình client/server, các nhà thiết kế đã tạo ra một số dịch vụ thông dụng trên mạng như:
finger, echo, mail, ftp . . . Các server của các dịch vụ này được dành sẵn các port xác định
mà không một quá trình nào được phép sử dụng. Các port này được gọi là well-known port
và do hệ thống cấp phát và quản lý. Thông thường, các chỉ số well-known port có giá trị từ
0 đến 1023. Các ứng dụng không được phép sử dụng giá trị port trong khoảng này. Ứng
dụng có thể dùng các giá trị port từ 1024 trở đi. Ví dụ: khi chúng ta cần tạo một socket mà
không cần quan tâm đến giá trị port, chúng ta có thể nhờ hệ thống cấp cho một giá trị port
còn trống. Thông thường các giá trị port mà hệ thông cung cấp cho ứng dụng khi có yêu cầu
nằm trong khoảng từ 1024 đến 5000. Còn khi chúng ta muốn chỉ định một giá trị port cho
socket, chúng ta sẽ có thể chọn giá trị từ 5000 trở đi. Vì trong vùng này xác suất mà port đó
đã bị chiếm là rất hiếm.
Vì vậy, khi thiết kế chúng ta muốn tạo một port cố định thì nên chọn socket lắng
nghe ở một port có giá trị lớn hơn 5000. Giá trị được chọn là 7699 nhưng mô hình của
chúng ta là : trong một chương trình vừa có đóng vai trò là client vừa là server nên ta chọn
port có thể thay đổi được trong khoảng từ 1024 đến 5000.
Khi muốn tạo liên kết, chúng ta sẽ tạo một socket và tiến hành connect vào socket
đang lắng nghe ở một địa chỉ và port lắng nghe. Khi socket listen nhận thấy có yêu cầu liên
kết, nó sẽ thông báo cho người sử dụng biết. Nếu nguời sử dụng đồng ý thì nó sẽ tiến hành
connect và việc trao đồi dữ liệu bắt đầu. Nếu người sử dụng từ chối thì ứng dụng sẽ thông
báo cho phía gọi lời từ chối và đóng liên kết lại.
Chúng ta nói thêm về địa chỉ khi liên kết. Do chương trình hiện thực trên môi trường
mạng Windows là môi trường mạng workgroup. Mỗi máy được xem như một host riêng lẻ.
Nếu trên mạng không có các server như server novell hay server NT thì chúng ta không thể
biết được các thông tin về một máy remote nếu chúng ta không tạo liên kết với máy đó. Vì
vậy, trong cơ chế liên kết, chúng ta chọn việc định địa chỉ để liên kết. Một máy muốn thiết
lập liên kết với một máy khác thì phải nhập thông số là địa chỉ IP của máy đó.
IV.2.3 CƠ CHẾ TRUYỀN NHẬN DỮ LIỆU
Khi viết một ứng dụng trên môi trường Windows, chúng ta phải lưu ý đến đặc điểm
của môi trường Windows là môi trường có kiến trúc message-driven. Windows được xem là
một môi trường có kiến trúc message-driven hay event-driven vì không một chương trình
nào trên windows có thể thực thi nếu không có một Thông báo hay một sự kiện kích khởi
nó. Trong môi trường Windows luôn tồn tại một vòng lặp message loop. Vòng message
loop này sẽ truy xuất các Thông báo từ các hàng chờ của các chương trình và tùy theo loại
Thông báo hay sự kiện, nó cho phép window procedure tương ứng thực thi. Vì vậy trên môi
trường Windows có thể tồn tại nhiều ứng dụng cùng một lúc mỗi ứng dụng có một hàng chờ
Thông báo riêng. Khi có một sự kiện xảy ra, hệ thống sẽ xác định xem sự kiện đó tuơng ứng
với ứng dụng nào và chuyển Thông báo đến hàng chờ của ứng dụng tương ứng đó. Tùy theo
loại Thông báo mà ứng dụng sẽ gọi chương trình tương ứng thực thi.
Môi trường windows 16 bits là môi trường nonpreemptive, có nghĩa là khi một ứng
dụng đang xử lý một Thông báo thì không một ứng dụng nào có thể thực thi được. Phải chờ
cho đến khi procedure của ứng dụng tiến hành xong công việc và trả về thì lúc đó procedure
tương ứng với Thông báo tiếp theo trong hàng chờ mới được thực thi. Trong khi đó các môi
trường Win32 như Windows98, WindowsNT lại thực thi theo cơ chế preemptive. Trong
môi trường này, việc procedure nào được thực thi là do hệ thống quyết định. Thật ra, trong
môi trường Win32, hệ thống định thời cho các thread thực thi. Thread chính là đoạn mã
thực thi của một chương trình nên các chương trình đều có cơ hội thực thi.
Khi winsock được thiết kế lần đầu tiên, các mô hình thiết kế được làm cho phù hợp
với cơ chế “ message-driven” và “ nonpreemptive” của Windows 16bits. Một số hàm socket
nguyên thủy khi thực thi cần một khoảng thời gian tương đối. Khi hàm thực thi rơi vào tình
trạng này, nó được gọi là bị tắc nghẽn. Khi một hàm bị tắc nghẽn, nó sẽ ngăn trở việc thực
thi của các hàm khác trong hệ thống. Trong hệ thống UNIX, môi trường mà socket được
thiết kế đầu tiên, các hàm blocking này không gây trở ngại cho hệ thống vì hệ thống sẽ
chiếm giữ các quá trình bị blocking và cho phép các quá trình khác thực thi.
Trong khi đó, hệ thống Windows 16 bits không có khả năng chiếm giữ các quá trình
blocking. Dẫn đến việc hệ thống không tiếp tục thực thi được vì các quá trình khác không
có cơ hội thực thi. Hệ thống phải chờ cho đến khi quá trình blocking hoàn tất công việc thì
mới tiếp tục thực thi được. Khi thiết kế winsock, các nhà thiết kế đã tính đến khả năng này.
Vì vậy họ có một giải pháp là đưa một đoạn mã đặc biệt vào hàm blocking để cho phép các
quá trình khác kiểm tra được hàng chờ Thông báo của mình. Tuy nhiên đây không phải là
một giải thuật hiệu quả.
Trong hệ thống socket của Berkeley các nhà nghiên cứu cũng đã lưu ý đến vấn đề
này khi thiết kế, và họ đã thiết kế các hàm nonblocking bên cạnh các hàm blocking. Chúng
ta xét một ví dụ là hàm send() của socket. Khi hoạt động ở chế độ blocking, hàm send() sẽ
gửi dữ liệu đi, hàm sẽ bị tắc nghẽn và nó chỉ trả về khi hoàn tất việc truyền dữ liệu, tức là dữ
liệu đã được nhận hoàn toàn. Còn nếu socket được tạo ra ở cơ chế bất đồng bộ, hàm send()
sẽ hoạt động ở chế độ non-blocking. Hàm send() sau khi gửi dữ liệu đi sẽ trả về ngay lập
tức. Và hệ thống sẽ phải gọi một hàm khác như select() để quan sát tình trạng của việc gửi
dữ liệu. Trên môi trường Windows chúng ta cũng có thể sử dụng các hàm non-blocking.
Tuy nhiên các nhà thiết kế winsock còn đưa ra các hàm bất đồng bộ.[3]
Các hàm bất đồng bộ được đưa ra dựa trên cơ chế hoạt động message-driven của môi
trường Windows. Chúng ta lấy ví dụ là các hàm gửi nhận dữ liệu. Việc gửi dữ liệu không
nhất thiết phải diễn ra ngay lập tức, và việc nhận dữ liệu sẽ bắt buộc chương trình phải chờ
trừ phi nó nhận được một hằng đặc biệt. Bằng cách tạo socket ở chế độ non-blocking để
dùng các hàm non-blocking và kết hợp với hàm WSAAssyncSelect(), ứng dụng sẽ nhận
được các message thông báo sự kiện để báo cho chương trình biết khi nào chương trình có
thể gửi dữ liệu đi hoặc đã có dữ liệu truyền đến cần đọc ra từ socket. Trong các khoảng thời
gian còn lại, khi không có thông báo các phần khác của hệ thống có thể thực thi được.
Các hàm bất đồng bộ rất phù hợp cho các hoạt động diễn ra trên môi trường
Windows 16 bits là môi trường nonpreemptive. Trong môi trường Win32 như Windows NT
hay Windows98 là môi trường preemptive các hàm blocking vẫn có thể sử dụng được. Tuy
nhiên việc dùng các hàm bất đồng bộ trên môi trường Win32 giúp chương trình đáp ứng tốt
hơn cho việc tương tác với người sử dụng. Một hàm blocking sẽ ngăn trở hệ thống đáp ứng
kịp thời cho các thao tác của người sử dụng. Điều này rất quan trọng trên một môi trường
giao diện như Windows. Vì vậy các hàm bất đồng bộ vẫn được sử dụng.
Vì môi trường Windows98 có hỗ trợ cơ chế lập trình song song thông qua việc định
thời thực thi cho các thread, do đó trong việc thiết kế, chúng ta chọn dùng cơ chế blocking
và thực hiện việc lập trình socket bằng các đối tượng do MFC cung cấp là các lớp
CAsyncSocket, CSocket, CSocketFile, CArchive. Việc chọn lập trình bằng công cụ này vì
có nhữnh đặc điểm sau:
Các lớp đối tượng đều do MFC hỗ trợ, phù hợp với cấu trúc chương trình được xây
dựng dựa trên các lớp đối tượng MFC. Ứng dụng được xây dựng trên các lớp đối tượng
MFC bằng các công cụ AppWizard, ClassWizard. Việc viết ứng dụng sẽ dễ dàng và đơn
giản hơn. Và khi ứng dụng có hỗ trợ socket thông qua các lớp đối tượng socket của MFC ở
trên, việc lập trình sẽ trở nên tiên lợi hơn.
Việc lập trình socket trên các lớp đối tượng thường dễ dàng và đơn giản hơn so với
việc lập trình bằng các hàm socket nguyên thủy được hỗ trợ bởi Windows SDK. Chúng ta
lấy một ví dụ như sau: tạo một socket và lắng nghe ở một port xác định.[6]
Lập trình bằng công cụ do Windows SDK hỗ trợ:
// Tạo Socket
SOCKET hSocket = socket ( int af = PF_INET, int type = SOCK_STREAM,
int protocol = 0);
// Ràng buộc socket vào một port cố định
SOCKADDR_IN sin;
u_short alport = IPPORT_RESERVED;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
for (;;) {
sin.sin_port = htons(alport);
if (bind(hSocket, (LPSOCKADDR)&sin, sizeof (sin)) == 0){
/* it worked */
}
if ( GetLastError != WSAEADDRINUSE) {
/* fail */
}
alport--;
if (alport == IPPORT_RESERVED/2 )
{
/* fail--all unassigned reserved ports are */
/* in use. */
}
}
// Lắng nghe lời gọi liên kết
int listen ( hSocket, int backlog = 5 );
Trong khi đó nếu chúng ta lập trình bằng các lớp socket do MFC hỗ trợ thì các công
việc phải thực thi như sau:
// Tạo lớp đối tượng
CSocket* m_pSocket = new CSocket;
// Tạo socket và ràng buộc vào một port xác định
m_pSocket->Create(nPort);
// Lắng nghe lời gọi liên kết
m_pSocket->Listen();
Khi lập trình chúng ta chọn các hàm blocking vì chúng được hỗ trợ bởi công cụ lập
trình serialize. Cơ chế serialize cho phép hệ thống đảm bảo việc truyền nhận dữ liệu trên
socket. Việc lập trình socket thông qua cơ chế serialize cũng đơn giản và dễ dàng hơn. Vì
chương trình được hiện thực trên môi trường Windows98 cho nên chúng ta sẽ thực thi vào
các hàm truyền nhận của socket. Vì vậy các hàm blocking cũng không ảnh hưởng nhiều đến
việc thực thi của chương trình cũng như trong toàn hệ thống.
Ngoài ra việc dùng cơ chế Serialize (Serialization là một quá trình đọc một đối tượng
dữ liệu từ đĩa hay ngược lại, ghi chúng lên dĩa. MFC hỗ trợ cơ chế serialization trong class
object vì vậy bất cứ đối tượng nào dẫn xuất từ object đều thừa hưởng cơ chế serialization)
cũng đồng nghĩa với việc chúng ta dùng giao thức TCP trong việc truyền nhận dữ liệu. Việc
dùng giao thức này giúp quá trình trao đổi dữ liệu được diễn ra tin cậy hơn. Dĩ nhiên phí tổn
hệ thống phải bỏ ra cũng cao hơn.
CHƯƠNG V
THIẾT KẾ CHƯƠNG TRÌNH
TRUYỀN ÂM THANH TRÊN MẠNG LAN
V.1 MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH
V.1.1 MÔI TRƯỜNG WINDOWS
Hệ điều hành Windows là hệ điều hành đa nhiệm , tức là nhiều chương trình có thể
chạy một lúc. Trong môi trường đa nhiệm, chương trình có thể chia làm nhiều phần nhỏ gọi
là các nhánh(thread) chạy đồng thời với nhau. Một chương trình khi chạy bao giờ cũng có
một nhánh chính, sau đó chương trình có thể tạo ra các nhánh con bằng cách gọi đến các
hàm CreateThread. Hệ điều hành sẽ làm nhiệm vụ điều khiển các nhánh này. Windows cho
phép đặt các mức ưu tiên cho các nhánh. Nhánh luôn được khởi tạo ở mức ưu tiên bình
thường, chương trình có thể thay đổi các mức ưu tiên của các nhánh bằng cách dùng hàm
SetThreadPriority tuỳ theo mức yêu cầu. Có các mức ưu tiên sau đây :
Idle
Below Normal
Normal
Above Normal
High
Readtime
Mức ưu tiên dành cho các ứng dụng chỉ hoạt động khi hệ thống rỗi, mức cuối cùng
dành cho các ứng dụng đòi hỏi thời gian thực.
IV.1.2 CÔNG CỤ LẬP TRÌNH
Do đặc điểm của chương trình là truyền nhậnh âm thanh trên mạng. Do đó lập trình
liên qua rất nhiều đến winsock. Hiện nay có rất nhiều ngôn ngữ hỗ trợ cho việc lập trình trên
mạng. Qua tìm hiểu em thấy rằng ngôn ngữ Visual C++ 6.0 có những hàm WinSock và
những cấu trúc được khai báo trong WINSOCK.H cho cả môi trường 16 cũng như 32 bit.
Những hàm Winsock được hiện thực trong WINSOCK.DLL (hay WSOCK32.DLL ứng với
bản 32 bit). Chương trình ứng dụng sẽ được link với WINSOCK.LIB (hay WSOCK32.LIB).
Chính vì vậy rất thích hợp cho lập trình các ứng dụng trên mạng.
Các Hàm TCP/IP
Sau đây là ý nghĩa chi tiết các tham số của các hàm TCP/IP. Để sử dụng các hàm
này, trong chương trình cần có chỉ thị [6] :
# include
Hàm accept :
·Cú pháp:
SOCKET PASCAL FAR accept (
SOCKET s,
struct sockaddr FAR * addr,
int FAR &addrlen);
·Chức năng:
Xác nhận mối nối của client. Nếu thành công, sẽ trả về socket được nối vào, ngược lại trả về
giá trị INVALID_SOCKET. Ý nghĩa các tham số :
Tham số Ý nghĩa
S Socket đang chờ client nối vào
Addr Bộ đệm nhận về địa chỉ client nối vào
Addrlen Chiều dài của địa chỉ trả về trong addr
Hàm bind :
·Cú pháp:
int PASCAL FAR bind (
SOCKET s,
const struct sockaddr FAR * name,
int namelen);
·Chức năng:
Kết buộc socket với port, đối với mỗi nghi thức ứng dụng chuẩn thì sẽ có một hằng số định
nghĩa sẵn trong winsock cho port của nghi thức đó. Nếu thành công trả về 0, ngược lại trả
về trị SOCKET_ERROR. Ý nghĩa các tham số :
Tham số Ý nghĩa
S Socket đang chờ kết buộc
Name Địa chỉ port mà socket sẽ được kết buộc
Namelen Chiều dài của tham số name
Hàm closesocket :
·Cú pháp:
int PASCAL FAR closesocket ( SOCKET s);
·Chức năng:
Sau khi hoàn tất quá trình trao đổi dữ liệu, ứng dụng gọi hàm closesocket để đóng socket đã
tạo.
Hàm connect :
·Cú pháp:
int PASCAL FAR connect (
SOCKET s,
const struct sockaddr FAR * name,
int namelen);
·Chức năng:
Gọi hàm connect để nối client vào server. Nếu thành công trả về 0, ngược lại trả về giá trị
SOCKET_ERROR. Ý nghĩa các tham số :
Tham số Ý nghĩa
s Socket đang chờ được nối với server
Name Địa chỉ server sẽ nối
Namelen Chiều dài của tham số name
Hàm inet_ntoa :
·Cú pháp:
char FAR* PASCAL FAR inet_ntoa (struct in_addr iadddr );
·Chức năng:
Nhận vào một địa chỉ internet iaddr và đổi thành địa chỉ internet dưới dạng chuỗi.
Hàm listen :
·Cú pháp:
int PASCAL FAR listen (
SOCKET s,
int backlog);
·Chức năng:
Tạo một hàng đợi tối đa backlog client trên port chờ được kết buộc với socket s.
Hàm recv :
·Cú pháp:
int PASCAL FAR revc (
SOCKET s,
char FAR* buf,
int len,
int flags);
·Chức năng:
Nhận dữ liệu từ socket. Sử dụng cho nghi thức TCP. Ý nghĩa các tham số :
Tham số Ý nghĩa
s Socket để nhận dữ liệu
buf Bộ đệm chứa dữ liệu đọc được
len Kích thước bộ đệm
flags Cách thực hiện thao tác
Hàm recvfrom :
·Cú pháp:
int PASCAL FAR recvfrom(
SOCKET s,
char FAR* buf,
int len,
int flags,
struct sockaddr* from,
int fromlen);
·Chức năng:
Nhận dữ liệu từ socket UDP, xác định địa chỉ của socket đã gửi dữ liệu. Ý nghĩa các tham
số :
Tham số Ý nghĩa
s Socket để nhận dữ liệu
buf Bộ đệm chứa dữ liệu đọc được
len Kích thước bộ đệm
flags Cách thực hiện thao tác, nhận giá trị
MSG_PEEK hay MSG_OOB
from Địa chỉ nơi dữ liệu được gửi đi
fromlen Chiều dài của fromlen
Hàm send :
·Cú pháp:
int PASCAL FAR send (
SOCKET s,
const char FAR* buf,
int len,
int flags);
·Chức năng:
Gửi dữ liệu qua socket. Sử dụng cho nghi thức TCP .Ý nghĩa các tham số:
Tham số Ý nghĩa
S Socket để gửi dữ liệu
Buf Bộ đệm chứa dữ liệu sẽ truyền
Len Kích thước bộ đệm
Flags Cach thực hiện thao tác
Hàm sendto :
·Cú pháp:
int PASCAL FAR sendto (
SOCKET s,
const char FAR* buf,
int len,
int flags,
const struct sockaddr *to,
int tolen);
·Chức năng:
Gửi dữ liệu đến một socket xác định. Sử dụng cho nghi thức UDP.Ý nghĩa các tham số:
Tham số Ý nghĩa
s Socket để gửi dữ liệu
buf Bộ đệm chứa dữ liệu sẽ truyền
len Kích thước bộ đệm
Flags Cách thực hiện thao tác
to Địa chỉ máy phải gửi dữ liệu
Tolen Chiều dài của tolen
Hàm setsockopt :
·Cú pháp:
int PASCAL FAR setsockopt (
SOCKET s,
int level,
int optname);
const char FAR* optval,
int optlen);
·Chức năng:
Đặt các thuộc tính tùy chọn cho socket.Ý nghĩa các tham số như sau:
Tham số Ý nghĩa
s Socket cần được đặt thuộc tính
level Mức định nghĩa thuộc tính, chỉ có thể nhận giá trị
SOL_SOCKET hay IPPROTO_TCP
optname Tên option muốn đặt, một số giá trị thông dụng :
-SO_BROADCAST: Cho phép truyền thông
broadcast trên socket.
-SO_LINGER : Chờ trước khi đóng socket nếu
còn dữ liệu chưa gửi.
Optval Bộ đệm chứa giá trị sẽ gán cho option, có thể là
BOOL hay struct, int tùy thuộc vào optname cụ
thể
Optlen Chiều dài của optlen
Hàm socket :
·Cú pháp:
SOCKET PASCAL FAR socket (
int addr,
int type,
int protocol);
·Chức năng:
Tạo một socket và trả về handle của socket nếu thành công, trường hợp có lỗi trả về giá trị
INVALID_SOCKET.Ý nghĩa các tham số như sau:
Tham số Ý nghĩa
addr Đặc tả dạng lưu trữ địa chỉ, hiện nay chỉ có thể nhận giá trị
AF_INET cho Internet
type Qui định lựa chọn TCP hay UDP
SOCKET_STREAM: ứng với TCP
SOCKET_DGRAM : ứng với UDP
Protocol Qui định nghi thức đặc biệt được dùng với socket, nhận giá trị
0 nếu không muốn sử dụng một nghi thức đặc biệt
Hàm WSAStartup :
·Cú pháp:
int PASCAL FAR WSAStartup(
WORD version,
LPWSADATA lpWSAData);
·Chức năng:
Đây là hàm phải gọi đầu tiên khi sử dụng socket. Hàm có nhiệm vụ khởi tạo thư viện liên
kết động Windows socket DLL.Ý nghĩa các tham số như sau :
Tham số Ý nghĩa
version Version cao nhất của Windows sockets API có
thể sử dụng
LpWSAData Chỉ đến cấu trúc WSADATA nhận về chi tiết
thực hiện của Windows Socket
Hàm WSACleanup :
·Cú pháp:
int PASCAL FAR WSACleanup(void);
·Chức năng:
Khi chấm dứt sử dụng các hàm socket phải gọi WSACleanup để đóng thư viện liên kết động
Windows Socket DLL.
V.2 THIẾT KẾ CHƯƠNG TRÌNH CPHONE
Từ những lý thuyết ở trên em xin xây dựng chương trình CPhone với mục đích chính
là truyền âm thanh trên mạng LAN như sau :
Công nghệ điện thoại ngày nay tuy phát triển mạnh và có nhiều phương pháp nén tín
hiệu âm thanh có hiệu quả. Nhưng ở Việt Nam hiện nay mới chỉ bắt đầu đưa vào sử dụng.
Trong điều kiện thời gian và trình độ có hạn nên đồ án này không thể đưa ra một mô hình
điện thoại Internet hoàn chỉnh với đầy đủ các phương pháp nén tiếng nói hiệu quả mà chỉ
đưa ra mô hình truyền nhận tiếng nói đơn giản nhất giữa hai thực thể máy tính.
V.2.1 MÔ HÌNH TRUYỀN ÂM THANH PC – PC TRÊN MẠNG
Việc xậy dựng một hệ thống sử dụng công nghệ điện thoại Internet theo mô hình thứ
2 và thứ 3 đã trình bầy trong phần mở đầu đòi hỏi quá trình nghiên cứu và thiết bị kỹ càng,
đầu tư công nghệ, vốn thích đáng mới có thể thực hiện được. Trong đồ án này thực hiện
theo mô hình thứ nhất là PC – PC, nó đơn giản hơn và không cầu đầu tư thiết bị mới, có thể
tận dụng được các thiết bị có sẵn, có thể thiết kế. Thử nghiệm hoàn chỉnh trong phạm vì đồ
án tốt nghiệp.
Mô hình PC – PC thức hiện hầu hết các thao tác bằng phần mềm, 2 máy tính chạy
cùng một phần mềm như vậy có thể dễ dàng mở rộng dần từng bước, có thể thử nghiệm trên
mạng LAN, sau đó có thể thử nghiệm trên mạng Internet.
Các bước cơ bản cho mô hình PC – PC gồm :
- ¢m thanh anolog từ microphone được chuyển thành digital tại
soundcard(PCM,8kHz,8bits/mẫu).
- Các mẫu được sao chép vào vùng đệm thành các khối có độ lớn nhất định.
- Dùng các thuật nén PCM để nén các khối đó.
- Thêm vào khối đã nến các thông tin khác
- Gửi khối dữ liệu sau khi dòng goi đó qua socket(TCP/UDP)
- Gói tín được truyền qua mạng vật lý đến đầu kia
- Loại bỏ các thông tin thêm vào, giải nén các khối dữ liệu đó, ghi khối dữ liệu âm
thanh được giải mã vào vùng đệm
- Chép các mẫu trên vào vùng đệm của soundcard
- Soundcard chuyển đổi các mẫu đó thành âm thanh.
V.2.2 XÂY DỰNG MÔ HÌNH CPHONE
Phần này trình bầy mô hình ứng dụng truyền âm thanh thoại cho phép trao đổi âm
thanh trên mạng LAN, gọi tên là CPhone.
Hinh V.1 Cấu trúc của mô hình CPhone
Mô hình này gồm các yếu tố chính như sau:
- Thiết bị ngoại vi và các kết nối vật lý (Physical transport) : Một số các thiết bị
ngoại vi cần thiết như : micro, loa, sound card. Các thiết bị này đảm nhận việc thu
, một phần của quá trình xử lý tiếng nói và phát tiếng nói. Các kết nối vật lý cung
cấp phương tiện trên đó các bit dữ liệu được truyền. Kết nối vật lý CPhone trong
mạng LAN, WAN, Internet có thể là cáp đồng trục, cáp quang, đường điện thoại
v.v...
- Giao thức liên lạc (TCP/IP) : Gồm một số các qui luật và nguyên tắc mà các thiết
bị trên mạng có thể liên lạc và làm việc với nhau. Giao thức sử dụng các kết nối
vật lý của mạng để truyền dữ liệu. Hệ thống CPhone sử dụng giao thức TCP/IP.
- Hệ thống phần mềm : Phạm vị nghiên cứu của đồ án này thì chương trình được
thiết kế có cấu hình tối thiểu là chỉ 2 máy tính cùng chạy một chương trình kết nối
với nhau qua mạng. Tín hiệu âm thanh từ Microphone qua soundcard được mã
hoá bằng phương pháp PCM,8kHz, 8bits/mẫu, được chia nhỏ thành từng đoạn có
độ dài nhất định(frame), các frame này được mã hoá theo các thuật toán, cuối
cùng gửi gói dữ liệu đã mã hoá đến socket của mạng và truyền gói này đến nơi
nhận.
Theo các mô hình đưa ra ở trên và phạm vị nghiên cứu của đồ án này thì chương
trình được thiết kế có cấu hình tối thiểu là chỉ 2 máy tính cùng chạy một chương trình kết
nối với nhau qua mạng. Tín hiệu âm thanh từ Microphone qua soundcard được mã hoá bằng
phương pháp PCM,8kHz, 8bits/mẫu, được chia nhỏ thành từng đoạn có độ dài nhất
định(frame), các frame này được mã hoá theo các thuật toán, cuối cùng gửi gói dữ liệu đã
mã hoá đến socket của mạng và truyền gói này đến nơi nhận.
Có thể chia thành 3 modul chính :
Modul xử lý âm thanh
Modul mã hoá(giải mã) âm thanh
Modul truyền nhận âm thanh.
Hình V.2 Các modul của chương trình CPhone
Khi chương trình này đưa vào thực tế sẽ góp phần vào giải quyết các công việc giữa
các phòng ban trong cơ quan được nhanh chóng và thuận tiện. Mục đích cuối cùng mà
chương trình có thể thực hiện được là có thể trực tiếp nói truyện với nhau trong mạng LAN
của cơ quan hay công ty nào đó...
V.2.3 MODUL THU VÀ PHÁT ÂM THANH
Modul xử lý âm thanh làm nhiệm vụ giao tiếp với soundcard, ghi vào bộ nhớ và
truyền cho modul II để mã hoá, đồng thời nhận các mẫu đã giải mã ở modul II để đưa ra
soundcard trong quá trình nhận.
Modul xử lý
âm thanh
Modul mã hoá
(giải mã)
Modul
truyền nhận
Mẫu tiếng nói
thu được
Gói dữ liệu sau
khi mã hoá
Mẫu tiếng nói đã
giải mã
Gói dữ liệu nhận
từ remote
Quá trình thu và phát tiếng nói thông qua vùng đệm (buffer), có 2 loại vùng đệm
trong Direct sound : Primary buffer và Secondary buffer. Primary buffer chứa tiếng nói mà
người nghe sẽ nghe thấy. Chỉ có duy nhất một Primary buffer và buffer này do DirectSound
tạo ra, secondary buffer chứa tiếng nói hoặc đoạn tiếng cần đưa ra. Chương trình có thể tạo
ra một hoặc nhiều Secondary buffer. Khi phát tiếng nói trong secondary buffer,
DerectSound sẽ chuyển tiếng nói sang Primary buffer và đưa ra soundcard. Chương trình
ứng dụng có thể dùng DirectSoundCapture để ghi tiếng nói vào một Capturebuffer. Cũng
như khi phát tiếng nói, chương trình có thể báo con trỏ đến một vị trí nào đó và khi nó đến
cuối của buffer thì nó tự động quay lại vị trí đầu của buffer.
Khi thao tác với các buffer, DirectSound cho phép khó một phần của buffer lại để
đảm bảo không bị ghi đè lên phần này.
Modul thu và phát tiếng nói nằm trong cùng một Record thread. Thread có mức ưu
tiên cao nhất. Mỗi khi có một dòng thông báo từ DirectSound gửi đến tức là khi con trỏ
Capture buffer đi đến vị trí quy định trước thì thread này sẽ được gọi đến. Lúc này, modul
thu tiếng nói sẽ khoá phần Capture buffer vừa ghi xong. Néu trong buffer của modul phát
tiếng nói có tín hiệu tiếng nói thì sẽ chép vào Secondary buffer của DirectSound.
Lưu đồ của modul thu và phát tiếng nói như sau :
Hình V.3 Lưu đồ của modul thu và phát tiếng nói
Bắt đầu
Khoá buffer
DirectSoun
Chép tín hiệu âm thanh vừa
lấy mẫu vào Record buffer
Play buffer
có dữ liệu
Chép play buffer vào
Secondary buffer
Mở khoá buffer
DirectSound
Kích hoạt
modul mã hoá
Kết thúc
đúng
sai
V.2.4 MODUL MÃ HOÁ VÀ GIẢI MÃ ÂM THANH
Modul mã hoá được kích hoạt sau khi modul VAD xác định tín hiệu âm thanh có
chức tiếng nói. Công việc của modul mã hoá gọi đến các thủ tục mã hoá tiếng
nói(GSM,LPC…). Sau đó, chuyển dữ liệu đã mã hoá vào Send buffer tồi kích hoạt modul
truyền. Modul nhận nằm trong thread mã hoá, có mức ưu tiên bình thường.
Sau đây là lưu đồ giải thuật modul mã hoá :
Bắt đầu
Kết thúc
Lấy một khung tiếng nói
từ Record buffer
Play buffer
có dữ liệu
Mã hoá theo phương
pháp PCM
Chép khung tiếng nói đã mã hoá
vào Sendbuffer
Kích hoạt
modul truyền
đúng
sai
Hình V.4 Lưu đồ giải thuật modul mã hoá
Modul giải mã tiếng nói được kích hoạt sau khi modul nhận được dữ liệu từ máy ở
xa. Loại bỏ Header, các dữ liệu thừa và đưa vào trong buffer nhận. Modul giải mã sẽ xá
định phương pháp giải mã tương ứng với phương pháp mã hoá được sử dụng ở bên truyền.
Sau đó chuyển dữ liệu đã mã hoá vào buffer phát tiếng nói.
Lưu đồ giải thuật modul giải mã như sau :
Hình V.5 Lưu đồ giải thuật modul mã hoá
V.2.5 MODUL TRUYỀN, NHẬN ÂM THANH
Modul này có nhiệm vụ giao tiếng giữa máy tính với mạng IP. Sử dụng giao thức
TCP/IP.
Khi buffer truyền có dữ liệu cần truyền đi, modul truyền sẽ kích hoạt . dữ liệu cần
truyền sẽ có thêm các header và các thông tin cần thiết khác. Modul truyền có mức ưu tiên
bình thường.
Bắt đầu
Kết thúc
Lấy một khung tiếng nói
từ buffer nhận
Buffer nhận
có dữ liệu
Mã hoá theo phương
pháp PCM
Chép khung tiếng nói vào buffer
phát
đúng
sai
Khi có dữ liệu đến, winsock sẽ gửi thông báo đến chương trình, chương trình nhận
được thông báo và kích hoạt modul nhận. Modul nàu có nhiệm vụ nhận dữ liệu, kiểm tra,
loại bỏ dữ liệu thừa cùng với header. Sau khi chuyển dữ liệu vào buffer nhận và kích hoạt
modul giải mã.
Lưu đồ giải thuật của modul truyền và nhận :
Hình V. 6 Lưu đồ giải thuật modul truyền
Bắt đầu
Kết thúc
Lấy một khung tiếng nói
từ buffer truyền
Buffer truyền
có dữ liệu
Truyền theo giao
thức TCP
đúng
sai
Bắt đầu
Kết thúc
Dùng giao thức
TCP
Lấy gói tin
đúng
sai
Kích hoạt modul
Có goi tin
chưa nhận
Hình V.7 Lưu đồ giải thuật modul nhận
V.3 THIẾT KẾ GIAO DIỆN CHƯƠNG TRÌNH CPHONE
Chương trình thực hiện với điều kiện hai máy đàm thoại phải cùng chạy chương trình
ứng dụng này.
Giao diện chính của chương trình :
Hình V.8 Giao diện chương trình CPhone
Khi muốn bắt đầu cuộc đàm thoại, người sử dụng vào menu kết nối và click vào nhập
IP. Điều này sẽ gọi hàm tạo kết nối giữa hai máy PC, hàm này hiển thị cửa sổ yêu cầu người
sử dụng nhập vào IP của máy cần kết nối. Sau khi đã nhập xong click connect, một thông
điệp yêu cầu kết nối sẽ gửi đến máy cần kết nối.
Hình V.9 Nhập địa chỉ IP
Nếu không có tín hiệu thì chưa cho phép quá trình đàm thoại. Khi có tín hiệu phản
hồi thì các hàm khởi động sound card, card mạng và socket được gọi và thực hiện cuốc đàm
thoại.
Hình V.10 Cuộc đàm thoại đang được thực hiện
IV.4 KẾT QUẢ THỰC NGHIỆM&NHẬN XÉT ĐÁNH GIÁ CHƯƠNG TRÌNH
IV.4.1 KẾT QUẢ THỰC NGHIỆM
Môi trường thử nghiệm đồ án này là mạng LAN, sử dụng công nghệ Ethernet 10
Mbps. Hiện tại truyền dữ liệu giữa các máy tính trong mạng LAN còn rất ít, phần lớn các tài
nguyên về dải thông của mạng còn chưa sử dụng. Phần giải thông còn lại này là rất lớn để
truyền tín hiệu tiếng nói, cũng vì lý do này nên độ trễ point – to – point trong mạng nhỏ và
tương đối đồng đều (hầu hết < 10ms), giá trị này rất nhỏ so với mức chấp nhận được là
200ms. Vì chất lượng của cuộc đàm thoại trong trường hợp này hầu như chỉ phụ thuộc vào
chất lượng của sound card.
IV.4.2 ĐÁNH GIÁ KẾT QUẢ
Qua thử nghiệm, em thấy chất lượng tiếng nói đầu ra không có gì khác biệt so với
chất lượng tiếng nói đầu vào, chất lượng âm thanh sau khi truyền là có thể chấp nhận được.
Như du sao đó chỉ là đánh giá qua cảm nhận của tai chúng ta mà thôi nên chưa thể khảng
định được chất lượng tiếng nói sau khi truyền có đạt tiêu chuẩn cho phép hay không. Nếu sử
dụng các máy đo các thông số liên quan đến chất lượng tiếng nói, phương pháp này đảm
bảo đánh giá chính xác chất lượng tiếng nói trong điều kiện chấp nhận được. Nhưng luận
văn này mới chỉ dừng lại ở mức độ đồ án tốt nghiệp và do điều kiện không cho phép nên
chưa có thể có được các thiết bị đo các thông số của tiếng nói. Vì vậy, chưa thể đánh giá
chất lượng âm thanh một cách chính xác được.
So với một số dịch vụ truyền âm thanh thoại hiện nay như : VoIP (dịch vụ 171) ,
Internet Telephony thì chất lượng âm thanh tốt hơn. Vì mô hình Cphone chỉ ứng dụng trên
mạng cục bộ nên tốc độ đường truyền rất cao, mặt khác khoảng cách rất ngắn. Tuy nhiên độ
trễ còn rất lớn song với mạng cục bộ thì vấn đề này vẫn có thể chấp nhận được. Còn đối với
các dịch vụ 171 hay Internet Telephony hiện nay thì chất lượng âm thanh giai đoạn đầu nói
chung không đạt được mong muốn đối với người sử dụng nhưng hiện tại chất lượng âm
thanh đã được cải thiện đáng kể và đặc biệt các ứng dụng này ngày càng được đưa vào phục
vụ cuộc sống.
KẾT LUẬN
Đồ án tốt nghiệp đã hoàn thành các nhiệm vụ đề ra trong phạm vi cho phép. Do điều
kiện về thời gian cùng như trình độ còn hạn chế nên đồ án này dừng lại ở mức nghiên cứu
một số phương pháp mã hoá tiếng nói và một số giao thức truyền tín hiệu trên mạng Internet
như TCP/IP, UDP. Trên cơ sở đó xây dựng phần mềm thử nghiệm truyền tiếng nói thoại
trên mạng LAN.
Đồ án này của em sử dụng các hàm có sẵn trong môi trường SDK từ đó xây dựng
ứng dụng CPhone có thể liên kết hai máy lại và thực hiện quá trình trao đổi âm thanh.
Phần thử nghiệm mới chỉ dừng lại ở việc xây dựng một ứng dụng truyền tiếng nói
giữa 2 thực thể PC theo giao thức TCP/IP và tiếng nói mới chỉ được mã hoá theo phương
pháp PCM nên tỷ số nén và tốc độ truyền chưa cao. Mặt khác, do mới chỉ đánh giá được
chất lượng tiếng nói theo phương pháp chủ quan nên chưa khảng định được điều gì về độ trễ
và chất lượng tiếng nói sau khi truyền. Thế nhưng những gì đạt được trong đồ án này chủ
yếu là nhằm giúp em có thể nắm bắt được vững hơn về mặt lý thuyết cũng như nắm quyền
kiểm soát, quyền điều khiển ứng dụng trong thực tế, các kết quả này rất qua trọng và hữu
ích cho mọi hoạt động nghiên cứu sau này cho dù những nội dung trong bài luận văn và
phần thử nghiệm chưa được đầy đủ và hoàn chỉnh như mong muốn.
Hiện nay, mô hình truyền tiếng nói trên mạng(VoIP) đang được nghiên cứu và phát
triển mạng trên thế giới nói chung và nước ta nói riêng, nhưng đây vẫn là một vấn đề rất
mới, đặc biệt hiện này đã ứng dụng được cho mạng Internet. Qua thử nghiệm cho thấy
hướng nghiên cứu có thể tiếp tục nâng cấp để đưa vào sử dụng, trước tiên với mô hình nhỏ
là dùng cho mạng cục bộ trong các phòng ban. Từ đó có thể phát triển và đưa vào sử dụng
trên diện rộng nhăm tăng chất lượng các cuộc đàm thoại, giảm giá thành cuộc gọi và để
thương mại hoá sản phẩm. Về lâu dài thì có thể nghiên cứu, xây dựng các bộ giải mã cho
ngôn ngữ tiếng việt; nghiên cứu ghép thêm các modul nhằm tạo các cuộc đàm thoại mật để
đảm bảo an toàn cuộc gọi.
PHẦN PHỤ LỤC
Các từ viết tắt, ký hiệu :
AbS : Analysis by Synthesis
ADPCM : Adaptive Differential Pulse Code Modulation
ATC : Adaptive Transform Coding
PABX : Private Automatic Branch eXchange
PCM : Pulse Code Modulation
GMS : Global System for Mobile telecommucations
ETSI : European Telecommunication Standards Institude
GSM : Global System for Mobile telecommunication protocol
MCI : Media Control Interface
TCP : Transmission Control Protocol
UDP : User Datagram Protocol
IP : Internet protocol
RIFF : Resource Interchange File Format
IMA : Interactive Multimedia Association
ICMP : Internet Control Message Protocol
LPC : Linear Prediction Coding
OSI : Open System Interconnection
Chương trình nguồn :
Đoạn chương trình thực hiên chức năng đợi (chạy nền)
UINT Server(LPVOID param)
{
const PORT=1967;
const NO_FLAGS_SET=0;
const MAXBUFLEN=500;
WSADATA Data;
SOCKADDR_IN SvrAddr,CIntAddr;
SOCKET SvrSock,CIntSock;
int AddrLen=sizeof(SOCKADDR_IN);
int status,nbyte,nbsend;
char bufferrecv[MAXBUFLEN],buffersend[80];
//Khởi tạo Windows Socket DLL
status=WSAStartup(MAKEWORD(1,1),&Data);
if(status!=0)
{
MessageBox(NULL,"ERROR: WSAStartup unsuccessful.",
"WSAStartup error!",MB_ICONEXCLAMATION);
return(-1);
}
memset(&SvrAddr,0,sizeof(SvrAddr));
SvrAddr.sin_port=htons(PORT);
SvrAddr.sin_family=AF_INET;
SvrAddr.sin_addr.s_addr=htonl(INADDR_ANY);
SvrSock=socket(AF_INET,SOCK_STREAM,0);
if(SvrSock==INVALID_SOCKET)
{
MessageBox(NULL,"ERROR: Socket unsuccessful.","Socket error !",
MB_ICONEXCLAMATION);
WSACleanup();
return(-1);
}
//Kết buộc socket với PORT
bind(SvrSock,(LPSOCKADDR)&SvrAddr,sizeof(SvrAddr));
//Chờ client nối vào
listen(SvrSock,1);
//Xác nhận mối nối khi có client nối vào
CIntSock=accept(SvrSock,(LPSOCKADDR)&CIntAddr,&AddrLen);
if(CIntSock==INVALID_SOCKET)
{
MessageBox(NULL,"ERROR: Connect unsuccessful.","Connect error !",
MB_ICONEXCLAMATION);
closesocket(CIntSock);
WSACleanup();
return(-1);
}
else
{
nbyte=recv(CIntSock,bufferrecv,MAXBUFLEN,NO_FLAGS_SET);
if((nbyte==0)||(nbyte==SOCKET_ERROR))
{
MessageBox(NULL,"Please the end conversation !","The conversation
!",MB_ICONEXCLAMATION);
closesocket(CIntSock);
WSACleanup();
return(-1);
}
else
MessageBox(NULL,bufferrecv,"The coversation !",MB_ICONINFORMATION);
strcpy(buffersend,"Receivered the signal. Please begin the conversation.");
nbsend=send(CIntSock,buffersend,strlen(buffersend)+1,NO_FLAGS_SET);
if(nbsend!=(int)strlen(buffersend)+1)
{
closesocket(CIntSock);
WSACleanup();
return(0);
}
return(0);
}
}
Đoạn mã thực hiện kết nối hai máy :
// Tạm dừng trạng thái chờ
SuspendThread(Server);
// Chuyển sang trạng thái gọi
m_DestAddrStr=dialog.m_DestAddrStr;
if(m_DestAddrStr=="")
{
MessageBox("ERROR: Hostname is unknown !","Error hostname !",
MB_ICONEXCLAMATION);
return(-1);
}
const PORT=1967;
const NO_FLAGS_SET=0;
const MAXBUFLEN=256;
WSADATA Data;
SOCKADDR_IN SockAddr;
unsigned long DestAddr;
int status,nbyte,nbrecv;
char SendBuf[50],RecvBuf[MAXBUFLEN];
// Khởi tạo Windows Socket DLL
status=WSAStartup(MAKEWORD(1,1),&Data);
if(status!=0)
{
MessageBox("ERROR :WSAStartup unsuccessf",
"Error WSAStarup !",MB_ICONEXCLAMATION);
return(-1);
}
// Nhận địa chỉ internet, chuyển sang dạng 4 byte
DestAddr=inet_addr(m_DestAddrStr);
//Khởi tạo cấu trúc SOCKADDR_IN
SockAddr.sin_addr=*(struct in_addr*)&DestAddr;
SockAddr.sin_port=htons(PORT);
SockAddr.sin_family=AF_INET;
// Tạo một socket
hSock=socket(AF_INET,SOCK_STREAM,0);
if(hSock==INVALID_SOCKET)
{
MessageBox("ERROR: Socket unsuccessful.","Error Socket",
MB_ICONEXCLAMATION);
WSACleanup();
return(-1);
}
// Nối vào máy chạy ứng dụng server
status=connect(hSock,(LPSOCKADDR)&SockAddr,sizeof(SockAddr));
if(status==SOCKET_ERROR)
{
MessageBox("ERROR: Connect unsuccessful !","Error connect !",
MB_ICONEXCLAMATION);
closesocket(hSock);
WSACleanup();
return(-1);
}
else
{
strcpy(SendBuf,"There is the signal send to your computer !");
nbyte=send(hSock,SendBuf,strlen(SendBuf)+1,NO_FLAGS_SET);
nbrecv=recv(hSock,RecvBuf,MAXBUFLEN,NO_FLAGS_SET);
if((nbrecv==0)||(nbrecv==SOCKET_ERROR))
{
MessageBox("Please end the conversation !", "The
conversation",MB_ICONEXCLAMATION);
closesocket(hSock);
WSACleanup();
return(-1);
}
else MessageBox(RecvBuf,"The conversation",MB_ICONINFORMATION);
}
// Kết thúc trạng thái gọi
// Khôi phục lại trạng thái chờ
ResumeThread(Server);
return(1);
}
Đoạn chương trình thực hiện chức năng ghi âm thanh từ micro
void CWAveIn::recordWaveData()
{
HWAVEIN hWaveIn;
HWND hwndApp=NULL;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HMMIO hmmio=NULL;
UINT wResult;
HANDLE hFormat=NULL;
WAVEFORMAT pFormat;
DWORD dwDataSize=1000;
// Mở thiết bị wave để phát.
if (!waveInOpen((LPHWAVEIN)&hWaveIn, WAVE_MAPPER,
(LPWAVEFORMATEX)&pFormat,
(LONG)waveInProc, DWORD(this), CALLBACK_FUNCTION))
{
MessageBox(
"Failed to open waveform input device.",
NULL,MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the waveform data.
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
if (!hData)
{
MessageBox( "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
if ((lpData =(HPSTR)GlobalLock(hData)) == NULL)
{
MessageBox( "Failed to lock memory for data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the header.
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (hWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox("Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(
"Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
// After allocation, set up and prepare header.
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwDataSize;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
waveInPrepareHeader(hWaveIn, lpWaveHdr, sizeof(WAVEHDR));
// Gởi khối dữ liệu đến thiết bị phát.
wResult = waveInAddBuffer(hWaveIn, lpWaveHdr, sizeof(WAVEHDR));
if (wResult = 0)
{
waveInUnprepareHeader(hWaveIn, lpWaveHdr,
sizeof(WAVEHDR));
GlobalUnlock( hData);
GlobalFree(hData);
MessageBox( "Failed to write block to device",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
wResult = waveInStart(hWaveIn);
if (wResult != 0)
return;
}
Đoạn chương trình play dữ liệu âm thanh
void CWAveIn::playbackWaveData()
{
HWAVEOUT hWaveOut;
HWND hwndApp =NULL;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HMMIO hmmio=NULL;
UINT wResult;
HANDLE hFormat=NULL;
WAVEFORMAT pFormat;
DWORD dwDataSize=1000;
// Mở thiết bị wave để phát.
if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
(LPWAVEFORMATEX)&pFormat,
(LONG)hwndApp, 0L, CALLBACK_WINDOW))
{
MessageBox(
"Failed to open waveform output device.",
NULL,MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the waveform data.
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
if (!hData)
{
MessageBox( "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
if ((lpData =(HPSTR)GlobalLock(hData)) == NULL)
{
MessageBox( "Failed to lock memory for data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the header.
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (hWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox("Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(
"Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
// After allocation, set up and prepare header.
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwDataSize;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
// Gởi khối dữ liệu đến thiết bị phát.
wResult = waveOutW
Các file đính kèm theo tài liệu này:
- Đồ án tốt nghiệp - Phân tích thiết kế hệ thống - Xây dựng ứng dụng truyền thông âm thanh trên mạng cục bộ.pdf