百度mp3近期活动频繁,各种尝试各种高端。其中的哼唱搜索,算是一次比较新的尝试了:http://mp3.baidu.com
哼唱搜索的目标是能让用户简单哼哼一段歌曲旋律即可搜索到相对应的歌曲。其最核心的包括两部分,一部分是语音分析,另一部分就是语音录制。这里就简单分享下语音录制这一部分。
关于音频录制,之前有过调研。在Flashplayer10.1中,可以直接获取到麦克风的数据:
var mp:Microphone = Microphone.getMicrophone(0);
mp.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleDataHandler);
在SampleDataEvent.SAMPLE_DATA事件中会携带音频流中的二进制数据,理论上将该二进制传给服务器即可得到分析结果。
不过目前Flashplayer10.1才刚刚推出,安装率不是很高。于是只能考虑传统的方案了——使用FMS(Flash Media Server)。
简单而言FMS是一款流媒体通信服务器。通过FMS,FlashPlayer可以很方便的与服务器进行通信,流的录制、播放以及分发,甚至是点对点连接。
哼唱搜索所用的,仅仅只FMS的录制功能,然后通过Socket连接解析服务器,和音频解析服务器通讯后,将数据返回给前端flash。这里仅和大家分
享下关键的几点:
1.音频录制
var ns:NetStream = new NetStream(nc);//nc为已连接到FMS的NetConnection
ns.addEventListener(NetStatusEvent.NET_STATUS, nsNetStatusHandler);
ns.attachAudio(microphone);//microphone为flash获取的当前麦克风
var streamName:String = "baidu_" + new Date().time + "_" + uint(Math.random() * 1000000000);
ns.publish(streamName, "record");//发布流
在发布流时,设置发布形式为”record”,即让服务器保存记录该流。这样在停止录音后,服务器才能将该文件发送给音频分析服务器。
这个过程遇到了一个小问题。当用户拒绝麦克风访问并记住该操作时,导致flash尝试获取麦克风时,flashplayer不会弹出是否容许访问麦克风面
板。
2.FMS服务器数据发送
FMS使用的脚本语言是Actionscript1.0,相对比较简单。在音频录制完毕后,服务器使用socket发送数据给音频分析服务器:
/**
* 发送数据到agent服务器,以便服务器读取流、解析、返回数据
*/
Client.prototype.sendDataToAgent = function(streamName) {
var clientObj = this;
var filePath = this.streamBasePath + streamName + ".flv";
filePath = file.toString();
//返回数据
var resultData;
//构建数据
var my_xml = new XML();
myData.attributes.filePath = filePath;
//发送数据
var xmlSocket = new XMLSocket();
xmlSocket.connect("127.0.0.1", 8484);//FMS和音频分析服务器是搭建在同一台服务器上面
xmlSocket.onConnect = function(success) {
trace("SocketOnConnect:" + success);
if (success == true) {
//发送数据
this.send(my_xml);
}
}
xmlSocket.onData = function(value) {
trace("SocketOnData:" + value);
resultData = value;
}
xmlSocket.onClose = function() {
trace("SocketnClose");
//回调客户端方法并传送服务器识别数据
clientObj.call("hummingSearchingResultHandler", null, resultData);
}
}
FMS和边缘服务器通信主要是两种方式,XMLSocket以及LoadVars。
XMLSocket是长连接,每次服务器向FMS发送数据后,都需要发送一个值为0的字节,作为数据发送完毕的标志。LoadVars是短连接,使用起来相对
比较简单:
myLoadVars = new LoadVars();
myLoadVars.onLoad = function(result){
trace("myLoadVars load success is " + result);
}
myLoadVars.load("....");
3.文件操作
在哼唱搜索中,有的时候用户在哼唱过程中就和服务器断开了连接。导致产生了冗余的音频录制文件。于是需要服务器在用户断开连接是删除相应
的文件。于是就设计到FMS的文件操作了。
FMS的文件操作就只有一个File类:
var filePath = this.streamBasePath + streamName + ".flv";
var file = new File(filePath);
if (file.exists) {
file.remove();
}
一个很重要的地方是,文件路径只能是相对路径,也就是说必须以“/”开头。比如var file = new File(“/test.flv”)。但是如果我的文件不是
在应用程序路径下怎么办,比如你要读取“F:/streams/test.flv”文件怎么办?
FMS可以设置一个虚拟目录,将一个目录名映射到一个绝对路径,在Application.xml配置文件的JSEngine节点中配置:
/streamPath;F:/streams/
这样,就可以通过var file = new File(“/streamPath/test.flv”)来操作“F:/streams/test.flv”文件了。
4.到此,最最重要的还没讲:FMS与前端Flash的通讯。
FMS和Flash的通讯非常灵活,可以通过NetConnection,已可以通过NetStream,还可以通过SharedObject。原理都是一样的,通过call方法,传递
需要调用的方法名,以及调用该方法的一系列参数:
//Flash端调用服务器方法,回调为null,参数为_streamName
nc.call("sendDataToAgent", null, _streamName)
....
//FMS调用Flash端方法
clientObj.call("hummingSearchingResultHandler", null, resultData);
深呼吸一口,一切尘埃落定,是时候自己去尝试下了..