(三)Android局域网内语音对讲 基于UDP语音传输

(三)Android局域网内语音对讲 基于UDP语音传输,第1张

之前研究了基于UDP的文字传输 点击打开链接 ,以及Android端的语音录制 点击打开链接 ,这篇文章就记录一下Android端局域网内的语音传输,简单的实现语音对讲,当然里面还存在着很多问题,包括语音不清晰啊、杂音多啊,不管了,先听见声音就行了。测试的时候两部手机,上图:

程序写了两个线程,一个用于录制AudioRecordThread,一个用于播放AudioTrackThread.

(一)录制与发送

@Override

public void run() {

if (mSocket == null)

return

try {

mStartTime = System.currentTimeMillis()

audioRec.startRecording()

while (flag) {

try {

byte[] bytes_pkg = buffer.clone()

if (mRecordQueue.size() >= 2) {

int length = audioRec.read(buffer, 0, minBufferSize)

//获取音量大小

mVolume = getAudioColum(buffer)

System.out.println(TAG + "= " + mVolume)

Message message = mHandler.obtainMessage()

message.arg1 = (int) mVolume

mHandler.sendMessage(message)

DatagramPacket writePacket

InetAddress inet = InetAddress.getByName(inetAddressName)

writePacket = new DatagramPacket(buffer, length, inet, PORT)

writePacket.setLength(length)

System.out.println("AudioRTwritePacket = " + writePacket.getData().toString())

mSocket.send(writePacket)

}

mRecordQueue.add(bytes_pkg)

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace()

}

}

audioRec.stop()

} catch (Exception e) {

e.printStackTrace()

}

}

里面包含了获取音量大小,便于在页面上面展示,方法参考了 点击打开链接

private double getAudioColum(byte[] buffer) {

double sumVolume = 0.0

double avgVolume = 0.0

double volume = 0.0

for (int i = 0i <buffer.lengthi += 2) {

int v1 = buffer[i] &0xFF

int v2 = buffer[i + 1] &0xFF

int temp = v1 + (v2 <<8)// 小端

if (temp >= 0x8000) {

temp = 0xffff - temp

}

sumVolume += Math.abs(temp)

}

avgVolume = sumVolume / buffer.length / 2

volume = Math.log10(1 + avgVolume) * 10

return volume

}

(二)接收与播放

@Override

public void run() {

if (mSocket == null)

return

//从文件流读数据

audioTrk.play()

while (flag) {

DatagramPacket recevPacket

try {

recevPacket = new DatagramPacket(buffer, 0, buffer.length)

mSocket.receive(recevPacket)

audioTrk.write(recevPacket.getData(), 0, recevPacket.getLength())

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace()

}

}

audioTrk.stop()

}

(三)主页面 接收按钮事件

@OnClick({R.id.btn_receive})

public void onViewClicked(View view) {

switch (view.getId()) {

case R.id.btn_receive:

if (btnReceive.getText().toString().equals("开始接收")) {

btnReceive.setText("停止接收")

try {

if (audioTrackThread == null) {

audioTrackThread = new AudioTrackThread()

}

new Thread(audioTrackThread).start()

} catch (SocketException e) {

e.printStackTrace()

}

} else {

btnReceive.setText("开始接收")

audioTrackThread.setFlag(false)

}

break

}

}

(四)发送按钮事件

ivSpeak.setOnTouchListener(new View.OnTouchListener() {

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)

@Override

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

//按下按钮开始录制

ivSpeak.setText("正在说话")

//显示录音提示

relativeLayout.setVisibility(View.VISIBLE)

try {

if (audioRecordThread == null) {

audioRecordThread = new AudioRecordThread(handler)

}

audioRecordThread.setInetAddressName(tvReceiveIp.getText().toString())

audioRecordThread.setFlag(true)

new Thread(audioRecordThread).start()

} catch (SocketException e) {

e.printStackTrace()

}

break

case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_CANCEL:

//松开按钮结束录制

ivSpeak.setText("按住说话")

relativeLayout.setVisibility(View.GONE)

audioRecordThread.setFlag(false)

mStopTime = audioRecordThread.getmStopTime()

mStartTime = audioRecordThread.getmStartTime()

creatMessageBean((mStopTime - mStartTime) / 1000, true)

break

}

return true

}

})

能语音聊天的软件有:

1、《YY》语音聊天软件的鼻祖,上线以来一直深受用户的喜爱。在软件里面有很多的聊天室,用户可以随机加入别人的聊天室,抢麦序唱歌聊天,玩法丰富。

2、《IS语音》视频互动软件,可以看视频也可以聊语音,帮助用户释放自己的激情。聊天室多种玩法,唱歌PK还是打视频PK都可以,PK主题由用户自己决定。

3、《久久语音》在平台上有很多的语音怪,她们声音好听个性十足,有萝莉音也有御姐音,你喜欢的声音里面都有,即时语音传输让大家用声音交友,用真情互动。

4、《耳旁语音》真人在线聊天,平台保证用户的资料都是真实的,语音聊天让用户处CP更加简单,支持多人同步语音,点唱功能还可以指定喜欢的声音来唱歌。

5、《以陌语音》不想打字的用户可以试一试这款聊天软件,在平台上大家都有机会找到一起聊天伙伴,甚至是心仪的对象,多人聊天让用户不在尴尬,不担心没有话题可以聊。

6、《小恩爱》非常适合情侣用户的专属聊天软件,两个人一起在软件上建立爱的小屋,通过连麦来玩游戏,越玩感情越深,异地情侣也可以因为即时的语音聊天感觉近在咫尺。

7、《探探》更加新潮的交友方式,滑动卡片就能匹配到心仪的聊天对象,如果匹配不成功的话是不能开展聊天的,所以大家可以放心自己会被陌生人打扰的问题。

传输语音和文件都可以通过Mswinsock 实现,只是速度方面我没有试过,我正巧最近也弄了点Mswinsock 相关的东西,代码发给你看看,希望有帮助

传送文件对于网络编程来说是基本的功能,比如远程控制软件。在编制一个软件时,我从网上下了很多传文件的程序,这些程序提供的传文件功能根本就不能用。传文本还可以,传二进制文件根本就不行。因此,作为一个基本的功能模块,有必要单独介绍一下。

首先,在VB中要传送字符串,你可以这样写:

Dim strData As String

strData = "Test"

Winsock1.SendData strData

但是如果你传送的二进制文件,你还能用String变量来存放吗?从理论上分析是不行的,我也做了实验,确实是不行的。文件虽然可以传,但是接受的文件和发送的不一样,原因可能是二进制文件里可以有任何"字符",但是不是所有的字符都可以放在String变量里。

除了String类型的变量,VB中其他类型的变量都只有几个字节长,难道一次只能发几个字节吗?那样岂不是要累死机器了!其实,情况没有那么悲观,我们完全可以使用数组来解决这个问题,就是使用byte数组。把要传送的文件都读到数组里,然后发送出去。程序如下:

FileName 为要传送的文件名,WinS为发送文件的WinSock控件。这是一个发送端的程序。

Public Sub SendFile(FileName As String, WinS As Winsock)

Dim FreeF As Integer ''空闲的文件号

Dim LenFile As Long ''文件的长度

Dim bytData() As Byte ''存放数据的数组

FreeF = FreeFile ''获得空闲的文件号

Open FileName For Binary As #FreeFile ''打开文件

DoEvents

LenFile = LOF(FreeFile) ''获得文件长度

ReDim bytData(1 To LenFile) ''根据文件长度重新定义数组大小

Get #FreeFile, , bytData ''把文件读入到数组里

Close #FreeFile ''关闭文件

WinS.SendData bytData ''发送数据

End Sub

接受端的程序如下:

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)

Dim bytData() As Byte

Dim f

f = FreeFile

Open strFileName For Binary As #f

ReDim bytData(1 To bytesTotal)

Winsock1.GetData bytData

Put #f, i, bytData

i = i + bytesTotal ''保证每次写都是在文件的末尾, i是个全局变量

Close #f

End Sub

这里有两个需要注意的地方,ReDim Preserve bytData(1 To LenFile),下标是从1开始的,如果你写成ReDim bytData( LenFile),下标就是从0开始了,数组就有LenFile+1长了。LenFile = LOF(FreeFile)中的LOF是获得文件长度的函数,是VB里带的,我见过很多例子用API,或者循环的读直到末尾来获取文件长度,这样都是很麻烦的,使用LOF函数就可以了。

这样的程序,即可以传送文本文件,也可以传送二进制文件。但是你有没有发现这个程序的问题呢?如果我要传送一个50M的文件呢?系统可以为bytData分配50M的内存空间吗?

于是笔者拿一个50M的文件做实验吧,接收到的文件和原来的文件不一样,比原来的大。问题出在那呢?

首先,根据文件大小重新定义bytData数组的大小本身就有问题,系统是不可能无限制的给数组分配空间的,即使可以,也会造成系统响应变慢。在传50M文件的时候,系统就跟死机了一样。那么怎么解决这个问题呢,一个自然的想法就是把数据分段传送。程序如下:

发送程序, iPos是个全局变量,初始值为0。这个变量保存着当前数据的位置。Const iMax = 65535是每个数据块的大小。

Dim FreeF As Integer ''空闲的文件号

Dim LenFile As Long ''文件的长度

Dim bytData() As Byte ''存放数据的数组

FreeF = FreeFile ''获得空闲的文件号

Open FileName For Binary As #FreeF ''打开文件

DoEvents

LenFile = LOF(FreeF) ''获得文件长度

If LenFile <= iMax Then ''如果要发送的文件小于数据块大小,直接发送

ReDim bytData(1 To LenFile) ''根据文件长度重新定义数组大小

Get #FreeF, , bytData ''把文件读入到数组里

Close #FreeF ''关闭文件

WinS.SendData bytData ''发送数据

Exit Sub

End If

''文件大于数据块大小,进行分块发送

Do Until (iPos >= (LenFile - iMax)) ''发送整块数据的循环

ReDim bytData(1 To iMax)

Get #FreeF, iPos + 1, bytData

WinS.SendData bytData

iPos = iPos + iMax ''移动iPos,使它指向下来要读的数据

Loop

''这里要注意的是,必须检查文件有没有剩下的数据,如果文件大小正好等于数据块大小的

'' 整数倍,那么就没有剩下的数据了

ReDim bytData(1 To LenFile - iPos) ''发送剩下的不够一个数据块的数据

Get #FreeF, iPos + 1, bytData

WinS.SendData bytData

Close #FreeF

下面是接收端的程序:

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)

Dim bytData() As Byte

Dim lLenFile As Long

Dim f

f = FreeFile

Open strFileName For Binary As #f ''strFileName是文件名

lLenFile = LOF(f)

ReDim bytData(1 To bytesTotal)

Winsock1.GetData bytData

If lLenFile = 0 Then ''lLenFile=0表示是第一次打开文件,这里有个问题,就是''如果如果该文件存在的话,就会出错,应该在打开前检查文件是否存在。(这里我省略了)

Put #f, 1, bytData

Else

Put #f, lLenFile + 1, bytData

End If

Close #f

End Sub


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11215654.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-14
下一篇 2023-05-14

发表评论

登录后才能评论

评论列表(0条)

保存