语音聊天室

生活百科 2023-01-26 17:49生活百科www.aizhengw.cn

语音聊天室

1 双方之间的网路连线 要进行频数据的传输,就是要建立数据连结。常用的通讯协定中,TCP较可靠,所以用在不允许数据丢失的套用上。而UDP则较多套用于处理速度要求较快、数据传输可靠性要求不是很高的套用上,如数据广播。通信协定的选择取决于我们所要做的套用的类型。怎样建立网路连线,稳定的接收和传送音频信号的数据流是关键。

基本介绍

  • 中文名语音聊天室
  • 外文名Voice Chat Room

原理介绍

在网路上传输音频的方面存在的问题主要可以归纳为以下几点
信号採集回放
在进行音频信号的採集中我们必须考虑到採样率的问题,声音信号的採样率有8Khz、16Khz、32Khz、44Khz等,每种数据採样虑产生的数据量都不一样,越高的採样率产生的数据量越大,所以我们要选择合适的採样率以适应网路的频宽。
信号编码解码
如果把直接採集到的音频信号数据流在网路上进行传输,它所占有的频宽也是十分大的,以8Khz的採样率採集14位的音频数据那幺就有以下这样的一个式子
语音聊天室
从中我们可以看出以这样的方式传输音频数据,每秒需要向网路中传送112kb的数据。所以。从节省频宽的角度考虑,我们很有必要对这样的数据进行压缩。对多媒体信号的压缩我们有许多可以选择的格式,如mp2、mp3、GSM等等,现在用的最多的是MP3格式。同样,我们这里也存在一个对压缩格式进行选择的问题,考虑到音频数据传输的及时性,对传输的音频数据质量的要求,以及各种压缩格式的压缩比率以及进行压缩和解压缩所要耗费的系统资源等方面问题,选择合适的压缩格式就显得尤为重要。

解决方法

下面就针对前面提出的问题讨论一下解决的办法
双方之间的网路连线
在这方面有其独特的优势,Java提供了丰富的网路类库的支持,可以轻鬆编写多种类型的网路通信程式。在我下面的例子中我就使用了TCP/IP协定,通过Java的Socket类进行编程2 音频信号的採集和回放以及音频数位讯号的编码与解码 在解决这两个问题的时候,在网上很幸运地通过一些文章的介绍,找到了Answer Machine 演示程式的原始码(由of j的Florian Bomers 和Matthias Pfisterer编写,网址在这个程式代码中,有几个解决我们问题所需要的类,而且作者将这些类封装的很好,我们基本不需要做什幺改动,只需要禁止其中的调试信息的输出就行了,更可贵的是它还封装了几种常见的音频格式。其中的GSM格式(Global System for Mobile Telecommunications)就是我们下面例子中採用的压缩格式,GSM格式可以将128kbps 的音频数据流 (16bit通过8k Hz的音频採样) 压缩为13kbps 的音频数据流,非常适合语音信号的传送,所以可谓是一石二鸟。

介绍

1、考米网
“考米网”是深圳市盈华讯方通信技术有限公司领先推出的能够电话聊天交友的网站,在网路交友的基础上增添电话聊天,提供一对一通话、多人通话、二人电话约会、自建会议聊天室、电话中传送的语音短讯,设定个性化个人资料等十几种功能。在不方便使用电脑的时候,也能通过手机、电话拨打400热线,随时和网上的好友保持联繫。
2、YY聊天
是一种YY团队语音工具,是多玩游戏网针对中文用户设计的多人语音群聊工具。 它是一款免费语音软体,稳定清晰的语音工具,用于游戏玩家交流等。即时通话,是一款不错的通讯软体。
3、ISpeak
ISpeak娱乐互动平台(简称IS),是国内专业游戏语音服务商,拥有上千万注册用户,每天活跃用户上百万,公会频道近10万个的规模,已经成为国内一流的语音互动平台。
IS以网路游戏用户为基础,提供专业的语音、聊天、社区论坛等。为用户提供免费的语音频道和无偿服务;为公会提供一个更好的实现自我平台,促进和扶持公会的成长;为厂商提供一个游戏推广,服务玩家的超大型互动娱乐社区。
4、QQ语音
腾讯公司一直为使用QQ的用户提供语音聊天服务。其语音聊天的缺点是通话质量差,只能电脑对电脑传播,所以一直没有大範围传播开。

原始码分析

我分析过这几个类的原始码,不得不佩服它的作者,每个类的原始码都很精炼,大家可以自己分析一下。好了下面就给大家讲讲这几个类,并且将它们用到的Java Sound API中的类和函式等一併做个简单介绍,让大家对Java Sound API中常用的类也有个大致的了解。由于Java Sound API中的类比较多。限于篇幅无法对所有用到的类做详尽的解释,以下内容只是简单提及了各个类的用途和使用规范,有关Java Sound API中类的具体介绍请大家访问这里[url=http://java./j2se/1.4.2/docs/api/]查找javax.sound.sampled的相关内容。
以下的提到几个档案是从Answer Machine 演示程式的原始码中提取出来的,由于是开放原始码的程式,大家在使用的时候请注意相关的

公共协定

AMAudioFormat类(封装在AMAudioFormat.java档案中)
AMAudioFormat类封装了CD、FM、TELEPHONE、GSM这四种质量的音频格式的参数,使用起来也非常简单,这样我们在使用Java Sound API时就不用自己去写那些複杂的代码了,但为了明白Java Sound API的原理,我们需要对它的代码做一下分析。它使用了Java Sound API中的AudioFormat这个类,这个类非常重要,在Java中对任何音频数据的使用都要实现通过它指定所需要使用的音频格式,AudioFormat类有一个嵌套的类AudioFormat.Encoding,实际上大部分对AudioFormat类的使用都是使用的这个嵌套的类。
AMAudioFormat类的重要方法
名称getLineAudioFormat
调用格式getLineAudioFormat(整型音频格式代号)
返回值 根据传递音频格式代号生成的AudioFormat对象。
说道这里大家可能要问了,那幺通过Java Sound API可以直接使用GSM格式吗?答案是比较複杂,但同样有解决的办法,作者在这里使用了的开源程式的类库-tritonus的GSM编码解码库。大家需要在这里下载tritonous_share.jar和tritonus_gsm.jar两个档案,并在AMAudioFormat类中引用,这样就完成了GSM格式的设定。需要告诉大家的是在对AMAudioFormat.java这个类进行编译后,我们的程式运行的时候就可以不需要tritonous_share.jar和tritonus_gsm.jar这两个档案的支持了。
(封装在AudioCapture.java档案中)2
AudioCapture类封装了从音频硬体捕获音频数据并自动编码为GSM音频压缩数据的过程,并且通过它的getAudioInputStream()方法提供给我们一个音频数据输入流,我们就可以直接将这个流传送到网路中。
AudioCapture 类的重要方法
名称getAudioInputStream
调用格式getAudioInputStream()
返回值AudioInputStream对象
AudioCapture 类使用了Java Sound API中的AudioInputStream、AudioFormat、AudioSystem这几个类和TargetDataLine、LineListener接口。除了AudioFormat类我再简单介绍一下其他的类
AudioInputStream 类是带有特殊音频格式和长度的InputStream类,它有两个构造方法,分别是AudioInputStream(InputStream stream, AudioFormat format,long length)和AudioInputStream(TargetData -Line line)。
TargetDataLine 接口是DataLine接口的一种,通过它就可以直接从音频硬体获取数据了,它有几个常用的方法,分别是open(AudioFormat format)、void open(AudioFormat format, int bufferSize)、int read(byte[] b, int off, int len)。
视频语音聊天室
AudioSystem 类是Java标準音频系统的入口点,在AudioSystem 类中使用他的getLine() 方法创建TargetDataLine对象。
LineListener接口用来对线路状态改变的时间进行监听,他的重要的方法是update(LineEvent event)方法。
(封装在AudioPlayStream.java档案中)
AudioPlayStream类与AudioCapture类刚好相反,它封装了GSM压缩音频数据的解码和音频信号的回放过程,提供给我们一个音频信号输出流。AudioCapture类用到的Java Sound API中的类它也基本都用到了,只是它使用了SourceDataLine接口而不是TargetDataLine接口
(封装在Debug.java档案中)
Debug类主要用来在调试时输出讯息,代码很少,后来我把其中输出信息的语句都禁止了,对程式运行没有影响。
为了方便使用以上的几个类,我们需要对它们进行编译和打包,编译时需要设定相关的编译环境,以下是我们需要用到的命令行
说明一下,我将以上提到的Java源码档案放在了am目录下,编译之后可以得到一个8k的am.jar档案,我们下一步所需要做的就是在我们的程式中引用这个包。

实例介绍

有了以上的基本的介绍,我就可以通过对我写的一个极为简单的语音对讲软体代码的解释让大家更清楚地了解一下这几个模组的具体使用方法,大家可以从中获得开发具有诸如网路电话、自动应答等功能的软体的类似方法,用于语音数据的传输。

程式的结构

整个程式分三层,作用分别如下
. 顶层: 用户界面
. 中间层: 控制层
. 底层: 传输层
程式有两个主要的类 (表)

类名描述

CallLink 网路传输层,用于接收或传送音频数据。
VoiceSender 作为第二个启动的执行绪提供从音频硬体捕获并编码好的数据给网路传输层。
程式的主类jphone使用了Runnable和ActionListener接口,主类除了基本的几个方法之外,还具有方法initAudioHardware()、ShowMSG、startPhone分别用于初始化AudioCapture类与AudioPlayStream类、显示程式状态和开始程式。主类jphone具有两个子类VoiceSender和CallLink。
子类VoiceSender同样使用了Runnable接口,它在程式中作为第二个启动的执行绪负责传送捕获到的音频数据。CallLink子类就是负责建立scoket连线,并且负责接收或传送网路数据、监听网路连线等功能的实现。它具有主要的方法是getInputStream()、getOutputStream()、listen()、open()、close()等。
为了让大家更清楚的了解程式的结构请大家看下面的类图。

程式工作流程

当程式启动时执行建立当前主类的实例,当按下呼叫按钮的时候执行startPhone()方法,startPhone()方法通过调用initAudioHardware()方法建立AudioCapture对象和AudioPlayStream对象的实例PhoneMIC和PhoneSPK, 紧接着在建立CallLink子类的实例curCallLink来与具有目标IP位址的计算机进行scoket连线后,startPhone()方法又将子类VoiceSender作为secondThread执行绪启动,然后又调用run()方法。 run()方法通过已经建立的CallLink子类的实例curCallLink监听网路上的数据(也就是等待别人的呼叫),一旦有音频数据到来curCallLink 实例就为AudioPlayStream 对象PhoneSPK 提供网路传来的音频数据,而PhoneSPK在一个循环中不断的将音频数据转换为音频信号,完成类似电话听筒的功能。
子类VoiceSender 就作为第二执行绪启动的时候,startPhone() 方法传递给它的参数是实例化的CallLink 子类curCallLink , 子类VoiceSender 通过实例化的AudioCapture 对象PhoneMIC 将音频信号压缩成GSM数据,并通过curCallLink 将音频数据传送到具有目标IP 地址的计算机上,完成类似电话受话器的功能。
在这里实例化的CallLink 子类curCallLink 就相当于两个电话之间的电话线,这样通过我以上的解释大家对程式的原理就有一个大概的了解了吧。
其中的音频数据传送执行绪和音频数据接收执行绪是同步的,不过考虑到网路的因素,可能在声音的传输上有一些延迟,不过由于延迟比较小对及时听到对方的话语影响不大。

编写代码摘要

在使用AudioCapture 类和AudioPlayStream 类的方法之前需要知道怎样初始化这两个类。在声明AudioCapture 对象的时候需要传递给它一个静态的整型值用于表达将音频信号压缩的方式,这个静态的整型常量可以是AMAudioFormat 类的以下四个值之一 FORMAT_CODE_CD 、FORMAT_CODE_FM 、FORMAT
所以声明AudioCapture 对象就要用一下的形式
private AudioCapture PhoneMIC null;
PhoneMIC new AudioCapture
FORMAT_CODE_GSM);
而声明AudioPlayStream 对象则不同,我们在初始化它的时候需要传递给它一个AudioFormat 对象,用于通知它我们所要播放音频的格式,这个AudioFormat 对象可以通过AMAudioFormat 类的getLineAudioFormat(格式参数值)方法获得,其中格式参数的取值和上面提到过的AMAudioFormat 的四个值相同,所以声明AudioPlayStream 对象就要用以下的形式
private AudioPlayStream PhoneSPK null;
在这之后就可以使用AudioCapture 和AudioPlayStream 对象的open() 方法打开音频捕获和音频回放通道完成它们的初始化了。如以下的形式
PhoneMIC.open();
PhoneSPK.open();
初始化完成之后要使AudioPlayStream 对象播放声音还需要以下过程,建立一个缓冲区(位元组数组)用于存放从网路传来的音频数据流,然后执行AudioPlayStream 对象的start() 方法使AudioPlayStream
对象开始声音的回放,这时执行一个while 循环,在循环中将音频流数据写入缓冲区,再使用AudioPlayStream对象的write()方法将缓冲区的数据还原成语音信号然后播放出来。如下面的例子
语音聊天
其中complete 的值用于标誌终止声音播放的异常原因。
类似的,初始化完成之后要使AudioCapture 对象捕获和压缩声音数据还需要其他的操作,声明一个InputStream 对象,赋其值为AudioCapture 对象的getAudioInputStream() 方法的返回值,执行
对象的start() 方法,然后在建立一个循环,将通过InputStream 的read() 方法得到的数据传送到网路上。例如以下代码
sendStream.write(compressedVoice,0,b);
......
通过使用CallLink 的几个方法,我们可以方便的传输和接收音频数据流。以下是它的代码
//使用套接字进行连线
void open() throws IOException, UnknownHostException
//打开网路连线
// 监听,等候呼叫
inServSock new ServerSocket(TALK_PORT);
inSock inServSock.accept();
public InputStream getInputStream()throws IOException
//返回音频数据输入流
H if (inSock != null)
return inSock.getInputStream();
else
return null;
publicOutputStreamgetOutputStream()throwsIOException
//返回音频数据输出流
if (outSock != null)
return outSock.getOutputStream();
else
return null;
void close() throws IOException
//关闭网路连线 ;
inSock.close();
outSock.close();
程式的代码总体有366 行,限于篇幅,这里就不一一列举了。
编译以及程式的使用方法
运行和编译本程式需要加上额外的环境参数,为了方便使用可以建立以下内容的批处理档案(假设程式所需要的包均在程式所在目录下的lib 资料夹中)
用于编译的批处理程式c.bat 的内容
javac -classpath .;lib\am.jar jphone.java
用于运行的批处理程式r.bat 的内容
java -classpath .;lib\am.jar jphone
启动时在A 计算机的IP 地址框内输入要进行连线的计算机B 的IP 地址,在计算机B 的IP 地址框内输入要进行连线的计算机A 的IP 地址,让后分别点击“拨出电话”按钮就可以进行连线了。别忘了接上麦克风和打开音箱电源,呵呵。
提醒大家,这里的IP 地址栏里预先存在的地址是127.0.0.1,也就是说,程式也可以和自己进行连线,点击“拨出电话”按钮,等8 秒左右敲敲你的麦克风,听到没有,是不是也有“嘣、嘣、嘣”的声音??

Copyright@2015-2025 www.aizhengw.cn 癌症网版板所有