声波通信开源项SinVoice介绍二

2024-09-03 06:38

本文主要是介绍声波通信开源项SinVoice介绍二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


    在上一篇的文章中,我们介绍了声波通信/验证的原理和基本使用,这一篇,我们将就一些细节进行谈论。

    再来一张项目的结构图


    SinVoicePlayer类是我们使用的时候直接接触的类,通过调用play()方法,我们就能将需要传输的数字播放出去,下面是这个类的代码实现

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2013 gujicheng 
  3.  *  
  4.  * Licensed under the GPL License Version 2.0; 
  5.  * you may not use this file except in compliance with the License. 
  6.  *  
  7.  * If you have any question, please contact me. 
  8.  *  
  9.  ************************************************************************* 
  10.  **                   Author information                                ** 
  11.  ************************************************************************* 
  12.  ** Email: gujicheng197@126.com                                         ** 
  13.  ** QQ   : 29600731                                                     ** 
  14.  ** Weibo: http://weibo.com/gujicheng197                                ** 
  15.  ************************************************************************* 
  16.  */  
  17. package com.libra.sinvoice;  
  18.   
  19. import java.util.ArrayList;  
  20. import java.util.List;  
  21.   
  22. import android.media.AudioFormat;  
  23. import android.text.TextUtils;  
  24.   
  25. import com.libra.sinvoice.Buffer.BufferData;  
  26.   
  27. /** 
  28.  *  
  29.  * @ClassName: com.libra.sinvoice.SinVoicePlayer 
  30.  * @Description: 声音播放类 
  31.  * @author zhaokaiqiang 
  32.  * @date 2014-11-15 下午12:56:57 
  33.  *  
  34.  */  
  35. public class SinVoicePlayer implements Encoder.Listener, Encoder.Callback,  
  36.         PcmPlayer.Listener, PcmPlayer.Callback {  
  37.   
  38.     private final static String TAG = "SinVoicePlayer";  
  39.   
  40.     private final static int STATE_START = 1;  
  41.     private final static int STATE_STOP = 2;  
  42.     private final static int STATE_PENDING = 3;  
  43.   
  44.     // 默认的间隔时间  
  45.     private final static int DEFAULT_GEN_DURATION = 100;  
  46.   
  47.     private String mCodeBook;  
  48.     // 用于存放使用CoodBook编码过的数字  
  49.     private List<Integer> mCodes = new ArrayList<Integer>();  
  50.   
  51.     private Encoder mEncoder;  
  52.     private PcmPlayer mPlayer;  
  53.     private Buffer mBuffer;  
  54.   
  55.     private int mState;  
  56.     private Listener mListener;  
  57.     private Thread mPlayThread;  
  58.     private Thread mEncodeThread;  
  59.   
  60.     public static interface Listener {  
  61.   
  62.         void onPlayStart();  
  63.   
  64.         void onPlayEnd();  
  65.     }  
  66.   
  67.     public SinVoicePlayer() {  
  68.         this(Common.DEFAULT_CODE_BOOK);  
  69.     }  
  70.   
  71.     public SinVoicePlayer(String codeBook) {  
  72.         this(codeBook, Common.DEFAULT_SAMPLE_RATE, Common.DEFAULT_BUFFER_SIZE,  
  73.                 Common.DEFAULT_BUFFER_COUNT);  
  74.     }  
  75.   
  76.     /** 
  77.      * 构造函数 
  78.      *  
  79.      * @param codeBook 
  80.      * @param sampleRate 
  81.      *            采样率 
  82.      * @param bufferSize 
  83.      *            缓冲区体积 
  84.      * @param buffCount 
  85.      *            缓冲区数量 
  86.      */  
  87.     public SinVoicePlayer(String codeBook, int sampleRate, int bufferSize,  
  88.             int buffCount) {  
  89.   
  90.         mState = STATE_STOP;  
  91.         mBuffer = new Buffer(buffCount, bufferSize);  
  92.   
  93.         mEncoder = new Encoder(this, sampleRate, SinGenerator.BITS_16,  
  94.                 bufferSize);  
  95.         mEncoder.setListener(this);  
  96.         mPlayer = new PcmPlayer(this, sampleRate, AudioFormat.CHANNEL_OUT_MONO,  
  97.                 AudioFormat.ENCODING_PCM_16BIT, bufferSize);  
  98.         mPlayer.setListener(this);  
  99.   
  100.         setCodeBook(codeBook);  
  101.     }  
  102.   
  103.     public void setListener(Listener listener) {  
  104.         mListener = listener;  
  105.     }  
  106.   
  107.     public void setCodeBook(String codeBook) {  
  108.         if (!TextUtils.isEmpty(codeBook)  
  109.                 && codeBook.length() < Encoder.getMaxCodeCount() - 1) {  
  110.             mCodeBook = codeBook;  
  111.         }  
  112.     }  
  113.   
  114.     /** 
  115.      * 将要加密的文本根据CodeBook进行编码 
  116.      *  
  117.      * @param text 
  118.      * @return 是否编码成功 
  119.      */  
  120.     private boolean convertTextToCodes(String text) {  
  121.         boolean ret = true;  
  122.   
  123.         if (!TextUtils.isEmpty(text)) {  
  124.             mCodes.clear();  
  125.             mCodes.add(Common.START_TOKEN);  
  126.             int len = text.length();  
  127.             for (int i = 0; i < len; ++i) {  
  128.                 char ch = text.charAt(i);  
  129.                 int index = mCodeBook.indexOf(ch);  
  130.                 if (index > -1) {  
  131.                     mCodes.add(index + 1);  
  132.                 } else {  
  133.                     ret = false;  
  134.                     LogHelper.d(TAG, "invalidate char:" + ch);  
  135.                     break;  
  136.                 }  
  137.             }  
  138.             if (ret) {  
  139.                 mCodes.add(Common.STOP_TOKEN);  
  140.             }  
  141.         } else {  
  142.             ret = false;  
  143.         }  
  144.   
  145.         return ret;  
  146.     }  
  147.   
  148.     public void play(final String text) {  
  149.         if (STATE_STOP == mState && null != mCodeBook  
  150.                 && convertTextToCodes(text)) {  
  151.             mState = STATE_PENDING;  
  152.   
  153.             mPlayThread = new Thread() {  
  154.                 @Override  
  155.                 public void run() {  
  156.                     mPlayer.start();  
  157.                 }  
  158.             };  
  159.             if (null != mPlayThread) {  
  160.                 mPlayThread.start();  
  161.             }  
  162.   
  163.             mEncodeThread = new Thread() {  
  164.                 @Override  
  165.                 public void run() {  
  166.                     mEncoder.encode(mCodes, DEFAULT_GEN_DURATION);  
  167.                     stopPlayer();  
  168.                     mEncoder.stop();  
  169.                     mPlayer.stop();  
  170.                 }  
  171.             };  
  172.             if (null != mEncodeThread) {  
  173.                 mEncodeThread.start();  
  174.             }  
  175.   
  176.             mState = STATE_START;  
  177.         }  
  178.     }  
  179.   
  180.     public void stop() {  
  181.         if (STATE_START == mState) {  
  182.             mState = STATE_PENDING;  
  183.             mEncoder.stop();  
  184.             if (null != mEncodeThread) {  
  185.                 try {  
  186.                     mEncodeThread.join();  
  187.                 } catch (InterruptedException e) {  
  188.                     e.printStackTrace();  
  189.                 } finally {  
  190.                     mEncodeThread = null;  
  191.                 }  
  192.             }  
  193.   
  194.         }  
  195.     }  
  196.   
  197.     private void stopPlayer() {  
  198.         if (mEncoder.isStoped()) {  
  199.             mPlayer.stop();  
  200.         }  
  201.   
  202.         // put end buffer  
  203.         mBuffer.putFull(BufferData.getEmptyBuffer());  
  204.   
  205.         if (null != mPlayThread) {  
  206.             try {  
  207.                 mPlayThread.join();  
  208.             } catch (InterruptedException e) {  
  209.                 e.printStackTrace();  
  210.             } finally {  
  211.                 mPlayThread = null;  
  212.             }  
  213.         }  
  214.   
  215.         mBuffer.reset();  
  216.         mState = STATE_STOP;  
  217.     }  
  218.   
  219.     @Override  
  220.     public void onStartEncode() {  
  221.         LogHelper.d(TAG, "onStartGen");  
  222.     }  
  223.   
  224.     @Override  
  225.     public void freeEncodeBuffer(BufferData buffer) {  
  226.         if (null != buffer) {  
  227.             mBuffer.putFull(buffer);  
  228.         }  
  229.     }  
  230.   
  231.     @Override  
  232.     public BufferData getEncodeBuffer() {  
  233.         return mBuffer.getEmpty();  
  234.     }  
  235.   
  236.     @Override  
  237.     public void onEndEncode() {  
  238.     }  
  239.   
  240.     @Override  
  241.     public BufferData getPlayBuffer() {  
  242.         return mBuffer.getFull();  
  243.     }  
  244.   
  245.     @Override  
  246.     public void freePlayData(BufferData data) {  
  247.         mBuffer.putEmpty(data);  
  248.     }  
  249.   
  250.     @Override  
  251.     public void onPlayStart() {  
  252.         if (null != mListener) {  
  253.             mListener.onPlayStart();  
  254.         }  
  255.     }  
  256.   
  257.     @Override  
  258.     public void onPlayStop() {  
  259.         if (null != mListener) {  
  260.             mListener.onPlayEnd();  
  261.         }  
  262.     }  
  263.   
  264. }  

     关于这个类,主要有以下几点:

    1.DEFAULT_GEN_DURATION是指的每个音频信号的持续时长,默认为0.1秒

    2.convertTextToCodes()方法是将需要编码的文本进行过滤,过滤规则就是CodeBook,如果要进行传输的数字不在CodeBook里面,程序就不会继续向下执行了

    

    虽然SinVoicePlayer类很重要,但是真正完成声音播放任务的并不是他,而是PcmPlayer类。因为源代码的一些命名很混乱很不明确,因此我修改了一些命名,如果想看原项目的同学不要感到惊讶。下面我们看一下这个类的实现。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2013 gujicheng 
  3.  *  
  4.  * Licensed under the GPL License Version 2.0; 
  5.  * you may not use this file except in compliance with the License. 
  6.  *  
  7.  * If you have any question, please contact me. 
  8.  *  
  9.  ************************************************************************* 
  10.  **                   Author information                                ** 
  11.  ************************************************************************* 
  12.  ** Email: gujicheng197@126.com                                         ** 
  13.  ** QQ   : 29600731                                                     ** 
  14.  ** Weibo: http://weibo.com/gujicheng197                                ** 
  15.  ************************************************************************* 
  16.  */  
  17. package com.libra.sinvoice;  
  18.   
  19. import android.media.AudioManager;  
  20. import android.media.AudioTrack;  
  21.   
  22. import com.libra.sinvoice.Buffer.BufferData;  
  23.   
  24. /** 
  25.  *  
  26.  * @ClassName: com.libra.sinvoice.PcmPlayer 
  27.  * @Description: PCM播放器 
  28.  * @author zhaokaiqiang 
  29.  * @date 2014-11-15 下午1:10:18 
  30.  *  
  31.  */  
  32. public class PcmPlayer {  
  33.   
  34.     private final static String TAG = "PcmPlayer";  
  35.     private final static int STATE_START = 1;  
  36.     private final static int STATE_STOP = 2;  
  37.     // 播放状态,用于控制播放或者是停止  
  38.     private int mState;  
  39.     private AudioTrack audioTrack;  
  40.     // 已经播放过的字节长度  
  41.     private long playedLen;  
  42.     private PcmListener pcmListener;  
  43.     private PcmCallback playerCallback;  
  44.   
  45.     public static interface PcmListener {  
  46.   
  47.         void onPcmPlayStart();  
  48.   
  49.         void onPcmPlayStop();  
  50.     }  
  51.   
  52.     public static interface PcmCallback {  
  53.   
  54.         BufferData getPlayBuffer();  
  55.   
  56.         void freePlayData(BufferData data);  
  57.     }  
  58.   
  59.     public PcmPlayer(PcmCallback callback, int sampleRate, int channel,  
  60.             int format, int bufferSize) {  
  61.         playerCallback = callback;  
  62.         // 初始化AudioTrack对象(音频流类型,采样率,通道,格式,缓冲区大小,模式)  
  63.         audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,  
  64.                 channel, format, bufferSize, AudioTrack.MODE_STREAM);  
  65.         mState = STATE_STOP;  
  66.     }  
  67.   
  68.     public void setListener(PcmListener listener) {  
  69.         pcmListener = listener;  
  70.     }  
  71.   
  72.     public void start() {  
  73.   
  74.         if (STATE_STOP == mState && null != audioTrack) {  
  75.             mState = STATE_START;  
  76.             playedLen = 0;  
  77.   
  78.             if (null != playerCallback) {  
  79.   
  80.                 if (null != pcmListener) {  
  81.                     pcmListener.onPcmPlayStart();  
  82.                 }  
  83.                 while (STATE_START == mState) {  
  84.                     // 获取要播放的字节数据  
  85.                     BufferData data = playerCallback.getPlayBuffer();  
  86.                     if (null != data) {  
  87.                         if (null != data.byteData) {  
  88.                             // 设置要播放的字节数据  
  89.                             int len = audioTrack.write(data.byteData, 0,  
  90.                                     data.getFilledSize());  
  91.                             // 首次进入,播放声音  
  92.                             if (0 == playedLen) {  
  93.                                 audioTrack.play();  
  94.                             }  
  95.                             playedLen += len;  
  96.                             // 释放数据  
  97.                             playerCallback.freePlayData(data);  
  98.                         } else {  
  99.                             LogHelper.d(TAG,  
  100.                                     "it is the end of input, so need stop");  
  101.                             break;  
  102.                         }  
  103.                     } else {  
  104.                         LogHelper.d(TAG, "get null data");  
  105.                         break;  
  106.                     }  
  107.   
  108.                 }  
  109.   
  110.                 if (STATE_STOP == mState) {  
  111.                     audioTrack.pause();  
  112.                     audioTrack.flush();  
  113.                     audioTrack.stop();  
  114.                 }  
  115.                 if (null != pcmListener) {  
  116.                     pcmListener.onPcmPlayStop();  
  117.                 }  
  118.             } else {  
  119.                 throw new IllegalArgumentException("PcmCallback can't be null");  
  120.             }  
  121.         }  
  122.     }  
  123.   
  124.     public void stop() {  
  125.         if (STATE_START == mState && null != audioTrack) {  
  126.             mState = STATE_STOP;  
  127.         }  
  128.     }  
  129. }  

     关于这个类,需要注意的是以下几点:

    1.PcmPalyer是通过AudioTrack类实现单频率播放的,在初始化AudioTrack对象的时候,需要穿很多参数,我在代码里面已经注释。在SinVoicePlayer中初始化PcmPlayer对象的时候,使用的是下面的参数进行的初始化

mPlayer = new PcmPlayer(this, sampleRate, AudioFormat.CHANNEL_OUT_MONO,

AudioFormat.ENCODING_PCM_16BIT, bufferSize);

    sampleRate是采样率,默认44.1kHZ,AudioFormat.CHANNEL_OUT_MONO是使用单声道播放,还有立体声也就是双声道模式,为了保证频率的一致,使用单声道比较合适。AudioFormat.ENCODING_PCM_16BIT是指使用16位的PCM格式编码,PCM也是一种声音的编码格式。


    2.在start()方法里面的while循环是为了不断的取出要播放的字节数据,audioTrack.play()方法只会执行一次,在stop()里面把mState赋值为STATE_STOP,while循环就会退出,从而执行下面audioTrack的停止方法,结束声音的播放。


    既然最后播放声音的重担落到了AudioTrack类的身上,那么我们就没有理由不去了解一下这个类了。


    AudioTrack是一个用来播放声音的类,构造函数中需要传下面这些参数

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,

            int bufferSizeInBytes, int mode)


    重点说一下第一个和最后一个参数的含义。

    AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
    STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。

    由于我们这里需要动态的写入不同的数据,因此,我们需要用MODE_STREAM模式,上面的代码中,是先write的数据,然后play(),其实正规的写法是先play(),然后通过write方法往AudioTrack里面写入字节数据即可。一开始我也疑惑呢,后来发现play方法只执行一次,而write方法会执行多次,即一边输入数据一边输出。


    在构造AudioTrack的第一个参数streamType和Android中的AudioManager有关系,涉及到手机上的音频管理策略。
Android将系统的声音分为以下几类常见的:
         STREAM_ALARM:警告声
         STREAM_MUSCI:音乐声,例如music等
         STREAM_RING:铃声
         STREAM_SYSTEM:系统声音
         STREAM_VOCIE_CALL:电话声音


    为什么要分这么多呢?例如在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,肯定不用再调节音量了。这可以让系统将这几种声音的数据分开管理。


    这篇文章先介绍到这里,下篇文章将介绍数字编码的实现细节。


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

这篇关于声波通信开源项SinVoice介绍二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132305

相关文章

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

金融业开源技术 术语

金融业开源技术  术语 1  范围 本文件界定了金融业开源技术的常用术语。 本文件适用于金融业中涉及开源技术的相关标准及规范性文件制定和信息沟通等活动。