声波通信开源项SinVoice介绍一


    在APP市场上,经常有一些充满新意的应用让我们眼前一亮,比如微信的面对面加好友,支付宝的声波支付等等,都是通过声波的方式进行握手通信,今天这篇文章将介绍声波通信和声波验证的实现原理和代码实现。

    首先介绍一下声波验证的原理。如果我们想发出声音,就必须震动,说话是声带在震动,手机能播放音乐是喇叭在震动。既然发出声音必须震动,那么就有震动快慢之分,我们把震动的快慢叫做声音的频率。频率低的声音低沉有力,能传播很远的距离,比如说大象之间通信就是利用次声波,也就是频率很低的声波进行的。而蝙蝠,我们都知道是通过超声波进行探路的,超声波就是震动频率比较高的声音。频率太高或者太低,人的耳朵都听不到,人耳的识别范围是20HZ-20000HZ。这里引出了一个单位,叫做赫兹(HZ),它是指一秒钟的震动次数。

    知道什么是声音的频率之后,我们就可以开始介绍声波通信的原理了。既然不同的声音有不同的频率,那么我们就可以假设1000HZ的声音代表1,2000HZ的声音代表2,以此类推,我们就可以用不同的频率代表不同的数字组合。在接收到声波之后,再根据不同的频率解析成我们需要的数据就好。

    如果我们想发出单频率的声音,我们就需要自己构造特定频率的正弦函数。手机喇叭在震动的时候,实际上是根据不同的电流带动鼓纸,进行不同频率的震动才发出声音的。而如果我们想要发出1000HZ的声音,我们就需要设计对应的正弦函数,来提供一定规律的电流。

    既然说到我们要自己设计正弦函数,还有几个名词我要解释一下:

    1.采样率

    是指每一秒要采集的声音的次数。因为平常我们说话的时候,产生的是模拟信号,就是时间连续的信号,如果我们想把语音录制下来怎么办呢?我们是做不到完完全全的都录制下来的,我们只能每隔一段时间采集一次数据,将模拟信号转化成数字信号,因此,采样点的多少就影响到语音的质量了。如果采样点多,那么质量就高,听起来就和原声的差别小;相对的,采样点少,质量就次,听起来就和原声不一样。这就是采样率的作用。

    2.采样定理

    上面说道,如果采样率高,录音的质量就高,那么,是不是采样率越高越好呢?当然不是。随着采样率的提高,虽然质量提高了,但是采样的难度也对应的增加了,而且,采样出来的数据需要存储,采样率越高,产生的数据文件就越大,因此质量高的音乐比一般的音乐体积大。所以,我们通常要选用一个合适的采样率。在信号处理领域有一个定理叫做“采样定理”,也称“奈奎斯特定理”,内容是:如果采样的频率高于信号最高频率的两倍,采样之后的数字信号就可以完整的保留下原始信号中的信息。因为人的听力范围在20HZ-20000HZ,所以一般采样频率在44.1kHZ,也就是一分钟44100次。

    在明白了这些预备知识之后,下面开始介绍开源项目SinVoice。




    上面是整个项目的结构,圈中的主要的类,下面把几个重要的类的功能和注意点介绍一下。为了便于理解,我自己添加了一些注释,并不是故意侵占原作者的版权哈。

    首先,我们先看一下到底怎么用,下面是MainActivity的代码:

[java]  view plain copy
  1. package com.example.sinvoicedemo;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.TextView;  
  10.   
  11. import com.libra.sinvoice.LogHelper;  
  12. import com.libra.sinvoice.SinVoicePlayer;  
  13. import com.libra.sinvoice.SinVoiceRecognition;  
  14.   
  15. /** 
  16.  *  
  17.  * @ClassName: com.example.sinvoicedemo.MainActivity 
  18.  * @Description: 声波通信 
  19.  * @author zhaokaiqiang 
  20.  * @date 2014-11-15 下午12:36:32 
  21.  *  
  22.  */  
  23. public class MainActivity extends Activity implements  
  24.         SinVoiceRecognition.Listener, SinVoicePlayer.Listener {  
  25.   
  26.     private final static String TAG = "MainActivity";  
  27.     // 最大数字  
  28.     private final static int MAX_NUMBER = 5;  
  29.     // 识别成功  
  30.     private final static int MSG_SET_RECG_TEXT = 1;  
  31.     // 开始识别  
  32.     private final static int MSG_RECG_START = 2;  
  33.     // 识别结束  
  34.     private final static int MSG_RECG_END = 3;  
  35.   
  36.     private final static String CODEBOOK = "12345";  
  37.   
  38.     private Handler mHanlder;  
  39.     // 播放  
  40.     private SinVoicePlayer mSinVoicePlayer;  
  41.     // 录音  
  42.     private SinVoiceRecognition mRecognition;  
  43.   
  44.     @Override  
  45.     protected void onCreate(Bundle savedInstanceState) {  
  46.         super.onCreate(savedInstanceState);  
  47.         setContentView(R.layout.activity_main);  
  48.   
  49.         mSinVoicePlayer = new SinVoicePlayer(CODEBOOK);  
  50.         mSinVoicePlayer.setListener(this);  
  51.   
  52.         mRecognition = new SinVoiceRecognition(CODEBOOK);  
  53.         mRecognition.setListener(this);  
  54.   
  55.         final TextView playTextView = (TextView) findViewById(R.id.play_text);  
  56.         mHanlder = new RegHandler((TextView) findViewById(R.id.regtext));  
  57.   
  58.         // 开始播放声音  
  59.         findViewById(R.id.start_play).setOnClickListener(new OnClickListener() {  
  60.             @Override  
  61.             public void onClick(View arg0) {  
  62.                 String text = genText(15);  
  63.                 playTextView.setText(text);  
  64.                 mSinVoicePlayer.play(text);  
  65.             }  
  66.         });  
  67.   
  68.         // 停止播放声音  
  69.         findViewById(R.id.stop_play).setOnClickListener(new OnClickListener() {  
  70.             @Override  
  71.             public void onClick(View arg0) {  
  72.                 mSinVoicePlayer.stop();  
  73.             }  
  74.         });  
  75.   
  76.         // 开始声音识别  
  77.         findViewById(R.id.start_reg).setOnClickListener(new OnClickListener() {  
  78.             @Override  
  79.             public void onClick(View arg0) {  
  80.                 mRecognition.start();  
  81.             }  
  82.         });  
  83.   
  84.         // 停止声音识别  
  85.         findViewById(R.id.stop_reg).setOnClickListener(new OnClickListener() {  
  86.             @Override  
  87.             public void onClick(View arg0) {  
  88.                 mRecognition.stop();  
  89.             }  
  90.         });  
  91.     }  
  92.   
  93.     // 获取长度为count且最大值为MAX_NUMBER的随机数  
  94.     private String genText(int count) {  
  95.         StringBuilder sb = new StringBuilder();  
  96.         int pre = 0;  
  97.         while (count > 0) {  
  98.             int x = (int) (Math.random() * MAX_NUMBER + 1);  
  99.             if (Math.abs(x - pre) > 0) {  
  100.                 sb.append(x);  
  101.                 --count;  
  102.                 pre = x;  
  103.             }  
  104.         }  
  105.   
  106.         return sb.toString();  
  107.     }  
  108.   
  109.     private static class RegHandler extends Handler {  
  110.   
  111.         private StringBuilder mTextBuilder = new StringBuilder();  
  112.         private TextView mRecognisedTextView;  
  113.   
  114.         public RegHandler(TextView textView) {  
  115.             mRecognisedTextView = textView;  
  116.         }  
  117.   
  118.         @Override  
  119.         public void handleMessage(Message msg) {  
  120.             switch (msg.what) {  
  121.             case MSG_SET_RECG_TEXT:  
  122.                 char ch = (char) msg.arg1;  
  123.                 mTextBuilder.append(ch);  
  124.                 if (null != mRecognisedTextView) {  
  125.                     mRecognisedTextView.setText(mTextBuilder.toString());  
  126.                 }  
  127.                 break;  
  128.   
  129.             case MSG_RECG_START:  
  130.                 mTextBuilder.delete(0, mTextBuilder.length());  
  131.                 break;  
  132.   
  133.             case MSG_RECG_END:  
  134.                 LogHelper.d(TAG, "recognition end");  
  135.                 break;  
  136.             }  
  137.         }  
  138.     }  
  139.   
  140.     @Override  
  141.     public void onRecognitionStart() {  
  142.         mHanlder.sendEmptyMessage(MSG_RECG_START);  
  143.     }  
  144.   
  145.     @Override  
  146.     public void onRecognition(char ch) {  
  147.         mHanlder.sendMessage(mHanlder.obtainMessage(MSG_SET_RECG_TEXT, ch, 0));  
  148.     }  
  149.   
  150.     @Override  
  151.     public void onRecognitionEnd() {  
  152.         mHanlder.sendEmptyMessage(MSG_RECG_END);  
  153.     }  
  154.   
  155.     @Override  
  156.     public void onPlayStart() {  
  157.         LogHelper.d(TAG, "start play");  
  158.     }  
  159.   
  160.     @Override  
  161.     public void onPlayEnd() {  
  162.         LogHelper.d(TAG, "stop play");  
  163.     }  
  164.   
  165. }  

     我们可以看出,声波播放和识别的代码封装的非常简单易用,我主要强调以下几点

    1.常量CODEBOOK是一个编码本,因为是这个功能可以商用,因此开源的代码中只给出了使用12345这5个数字进行编码的实例,所以这个常量不要修改。

    2.SinVoicePlayer和SinVoiceRecognition是两个非常重要的类,前者可以实现将数字转化成单频率的音频进行输出,后者则可以根据音频进行识别。我们可以设置监听器,来监听识别成功的事件回调。

    3.genText(int count) 方法是为了获取一个长度是count的随机数,而且这个随机数是有要求的,因为示例代码只实现了1到5的编码和解码,因此,生成的随机数必须在1到5之间才能进行正确的编解码,所以使用MAX_NUMBER进行随机数的大小控制

    如果只是想简单的使用这个功能,了解上面的知识之后,就完全可以用了,下一篇文章中,我将介绍实现过程中的一些细节问题,下一篇再见。

    项目的Github地址:https://github.com/JesseGu/SinVoice

     原文地址:http://blog.csdn.net/zhaokaiqiang1992

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值