zoukankan      html  css  js  c++  java
  • android音频发生器

    这是前一段时间遇到的问题了, 想用android手机的音频口发送消息,消息内容可以是字符串,可以是命令,甚至可以是文件。由于智能手机的普及,今后在工业领域可能会推广,所以在这里分享一下。

    现在开始:

    一(语文是硬伤,标题想不出来):

    本例只实现了发送字符串的功能(用的是方波标记),而且界面布局很简单,贴图:

    点击【send】将会发送出一段噪音,噪音的内容是用你所输入的字符串编码过来的,用的是Ascall码转换成高低电频,当然在手机的耳机插口需要插入一个自己开发的信号转换器,接收端需要安装配套的信号接受器,而且成本很低,似乎是用单片机开发的。好吧,露馅了,我不懂硬件……

     


    绑定button。

    [java] view plaincopy
     
    1. listener = new Listener();  
    2.         play = (Button)findViewById(R.id.play);  
    3.         text = (EditText)findViewById(R.id.text);  
    4.         play.setOnClickListener(listener);  


    listener的内容:

    [java] view plaincopy
     
    1. private class Listener implements OnClickListener  
    2.     {  
    3.   
    4.   
    5.         @Override  
    6.         public void onClick(View arg0) {  
    7.             // TODO Auto-generated method stub  
    8.             sendme = text.getText().toString();  
    9.             Log.d("!!!!!!","listener");  
    10.             if(sendme != ""&& sendme != null)  
    11.             {     
    12.                 if(thread == null)  
    13.                 {  
    14.                     thread = new AudioThread();  
    15.                     thread.start();  
    16.                 }  
    17.                 else  
    18.                     thread.letRun();  
    19.             }  
    20.               
    21.         }  
    22.           
    23.     }  


    thread的内容:

    [java] view plaincopy
     
    1. class AudioThread extends Thread{  
    2.   
    3.           
    4.         @Override  
    5.         public void run() {  
    6.             // TODO Auto-generated method stub  
    7.             while(true)  
    8.             {  
    9.                 if(isrun)  
    10.                 {  
    11.                     Log.d("!!!!!!","runner");  
    12.                     audioplayer = new AudioSend();  
    13.                     audioplayer.play(sendme);  
    14.                     try {  
    15.                         sleep(1000);  
    16.                     } catch (InterruptedException e) {  
    17.                         // TODO Auto-generated catch block  
    18.                         e.printStackTrace();  
    19.                     }  
    20.                     isrun =false;  
    21.                 }  
    22.                   
    23.             }  
    24.         }  
    25.         public void letRun()  
    26.         {  
    27.             Log.d("!!!!!!","letrunner");  
    28.             isrun = true;  
    29.         }  
    30.           
    31.           
    32.   
    33.     }  



    已经发现重点就在audioplayer这个对象,这个是自己编写的audiosend类构造的,运用的是AudioTrack,查看api就可以发现这个类有个write()方法,方法是重载的:write(byte【】,int,int)或者是write(short【】,int,int),那么数组中包含的究竟是什么?

     

    二:数组内容解析

    先来了解下耳机的构造:(耳机够着此段转载于http://blog.csdn.net/xl19862005/article/details/8522869

    我们知道,耳机是用来听音乐,打电话的,既然是和声音相关的,那么耳机线上传输的就是音频信号,常见的音频信号一般都是在100Hz——10KHz左右的范围内,那么手机里面的音频输出系统(DA和音频功放)的幅频特性(也既带宽)一定也是在这个范围(这是本人的猜想,由于设备和仪器有限,没有进行系统的测试,有兴趣的朋友可以用相关的测试仪器测测),那么,既然有带宽,好家伙,我们就可以通过努力在这个频带内实现我们的通信信道了!另外值得提的一点是,耳机线上传输的音频信号是交流的!

        下面我们来看看市面上常见的耳机座(公头)的引脚定义,android手机上用的耳机大多都是3.5mm的四芯座,在这四个芯中,分别是:地、左声道、右声道和线控开关(MIC)

    1、国家标准


    发现其实耳机接受的就是电流,那么猜测那个数组中包含的应该就是电流的强度。那么我们只需要将所要发送的内容转换为电流的强度来标记,并且在接受端按照相同的方式来解码即可。

    三:audiosend类内容:

    [java] view plaincopy
     
    1. <span style="font-size:14px;">public class AudioSend {  
    2.     static int baudRate = 4800;  
    3.     static int maxRate = 48000;  
    4.     static int delayBit = 0;  
    5.     private static byte ihigh = (byte) (-128);  
    6.     private static byte ilow = (byte) (16);  
    7.       
    8.     AudioTrack audioplayer;  
    9.     static int minSize;  
    10.       
    11.       
    12.     static byte[] getBuffer(String str)  
    13.     {  
    14.         int bytesinframe = delayBit + 10;//delay + 8bit + 一个标识开始的位 + 一个标识结束的位  
    15.         byte[] sendme = str.getBytes();  
    16.         int n = maxRate/baudRate;  
    17.         boolean[] bits = new boolean[sendme.length * bytesinframe];//  
    18.         byte[] waveform = new byte[(sendme.length*bytesinframe* n)]; //防止失真,延长每个波的变化的播放时间  
    19.       
    20.         Arrays.fill(bits, true); //当其不断传出电流的时候标志着无信息传送,一旦有低压电流标志开始传送数据  
    21.   
    22.         int i,m,k,j = 0;  
    23.         for (i=0;i<sendme.length;++i)  
    24.         {  
    25.             m=i*bytesinframe;  
    26.             bits[m]=false;  
    27.             bits[++m]=((sendme[i]&1)==1);//位操作,也可以先转换成数字再用 Integer.toBinaryString  
    28.             bits[++m]=((sendme[i]&2)==2);  
    29.             bits[++m]=((sendme[i]&4)==4);  
    30.             bits[++m]=((sendme[i]&8)==8);  
    31.             bits[++m]=((sendme[i]&16)==16);  
    32.             bits[++m]=((sendme[i]&32)==32);  
    33.             bits[++m]=((sendme[i]&64)==64);  
    34.             bits[++m]=((sendme[i]&128)==128);  
    35.             //加上延时的位  
    36.             for(k=0;k<bytesinframe-9;k++)   
    37.                 bits[++m]=true;  
    38.         }  
    39.   
    40.         //转换成需要的byte数组  
    41.         for (i=0;i<bits.length;i++)  
    42.         {  
    43.             for (k=0;k<n;k++)  
    44.             {  
    45.                 waveform[j++]=(bits[i])?((byte) (ihigh)):((byte) (ilow));  
    46.               
    47.             }  
    48.         }  
    49.   
    50.   
    51.         bits=null;  
    52.         return waveform;  
    53.     }  
    54.     public void play (String str)  
    55.     {  
    56.         byte[] send = getBuffer(str);  
    57.         minSize = AudioTrack.getMinBufferSize(4800,AudioFormat.CHANNEL_OUT_MONO  
    58.                 ,AudioFormat.ENCODING_PCM_16BIT);  
    59.         audioplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 4800, AudioFormat.CHANNEL_OUT_MONO,  
    60.                         AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM);  
    61.           
    62.         audioplayer.play();  
    63.         audioplayer.write(send, 0, send.length);  
    64.         audioplayer.stop();  
    65.         audioplayer.release();  
    66.     }  
    67.       
    68. }  
    69. </span>  

    在够着audiotrack的时候

    [java] view plaincopy
     
    1. <span style="font-size:14px;">minSize = AudioTrack.getMinBufferSize(4800,AudioFormat.CHANNEL_OUT_MONO  
    2.                 ,AudioFormat.ENCODING_PCM_16BIT);  
    3.         audioplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 4800, AudioFormat.CHANNEL_OUT_MONO,  
    4.                         AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM);</span>  

    第一行其实是获取最小缓冲区大小,阐述按顺序依次是采样率,声道,和采样精度,在这里简单讲一下,如果还不懂还是自己google吧

    如果画成坐标图,横轴是时间,纵轴是电流的话:

    采样率就是每秒钟要发送多少个点过去,也就是告诉audiotrack对象每秒需要从我所write的数组中提取多少个点,当然在播放音乐的时候采样率越高就会音质效果越好。我们来极端假设一下,如果采样率是1,(其实android上只能是4800,44100,48000)那么意味着你一秒这个区段听到的声音都是一样的,不敢想象,它会吧”忐忑“播成什么样。

    声道不废话了

    采样精度就是电流的强度的上限和下限,当然也是越大音质越好,目前似乎只能是8BIT和16BIT,应该也就是这个原因所以write接受的是byte和short数组吧。

    接下来将编码:

    对于编码也就是我的代码的getbuffer方法了。

    先来讲一下通信的协议吧。如图所示通信协议中每十个位标记位发送的一个字母,开始位定义成低电流终止位定义成高电流(我们没有定奇偶校检位),中间的八位来定义字母的内容(也就是字母转换成Acall码之后的内容)但是每个电流如果用1和0表示的话差别太小了,单片机很可能检测不到,或者检测失误,所以我将高电流定义为-128,低电流定义为16,代码先将每个字母转化成对应的ascll码,然后再将它转换成对应的高低电流的波峰和波谷。

    对于

    [java] view plaincopy
     
    1. <span style="font-size:14px;">//转换成需要的byte数组  
    2.         for (i=0;i<bits.length;i++)  
    3.         {  
    4.             for (k=0;k<n;k++)  
    5.             {  
    6.                 waveform[j++]=(bits[i])?((byte) (ihigh)):((byte) (ilow));  
    7.               
    8.             }  
    9.         }  
    10. </span>  
    目的是为了保真而延时,也就是将每个波的变化延长k个单位时间,k呢是48000/4800得来的(其实这块是总监要求的我不是很懂),我猜测,应该是android手机固定的是播放48000的采样点,即便你定义成4800也必须自己做延时,但是不对啊,那还定义那个参数干嘛?或者是单片机只能每秒接受480个点,也不对,因为那样的话k应该等于4800/480而不是48000/4800(虽然结果一样)。具体我也不知道,应该是总监错了,公司硬件还没做好,过段时间才能测试,如果出错了我再回来修改。
    好了,终于写完了。
  • 相关阅读:
    音频文件转码工具文档 目录 1. 音频文件转码 1 1.1. 简介 1 1.2. 转换命令示例 2 1.3. wav 文件转 16k 16bits 位深的单声道pcm文件 2 1.4. mp3 文件转
    Atitit 调用百度语音识别 目录 1. 建立一个音频app项目,获得appid kersec 1 2. 直接使用JAR包步骤如下: 1 2.1. public class baiduAudio
    Atitit 传媒学院专业与课程表艾提拉总结 目录 1. 媒体分为感觉媒体、表示媒体、表现媒体、存储媒体和传输媒体 1 1.1. 1、感觉媒体 如文字、数据、声音、图形、图像等。 1 1.2. 表示
    Atitit 剪贴板数据类型 DataFlavor 目录 1. HtmlFlavor 1 1.1. allHtmlFlavor 1 1.2. selectionHtmlFlavor 1 1.3. fr
    Atitit 解析m4a文件的元数据标签音乐名,歌手 专辑 年代等信息 java版本 目录 1.1. 自己解析mp4 m4a结构 1 1.2. 格式返回 1 1.3. /bookmarksHtmlE
    Atitit 音频资料与音乐库管理系统功能 目录 1. 通用功能区 2 1.1. 批量处理功能文件夹遍历 2 1.2. Zip文件遍历与读取 2 1.3. Rar文件遍历与读取 2 1.4. She
    Atitit java播放mp3 目录 1.1. 不能直接支持mp3播放。。需要解码播放转化为pcm 1 1.2. 使用\javalayer类库播放 3 1.3. ,就是普通的java sound
    Atitit 加强学生就业的规划与艾提拉的治学理念 目录 1. 思路的转换 1 1.1. 发展内需为主模型 vs 外贸模式 1 1.2. 批发模式vs 零售模式vs 1 1.3. 天堂模式vs地狱模
    Atitit sql的执行功能 目录 1. 主要流程 1 1.1. 获取conn,执行sql取得结果, 1 1.2. Orm类的执行(hb mybatis为例 1 2. 常见sql执行框架与类库 1
    Atitit ffmpeg功能表 多媒体处理类库工具 音频视频 1.1.ffmpeg音视频合成  1.2.Atitit 视频音频分离 提取法 1.3.ffmpeg对视频封装和分离 使用ffmpeg对
  • 原文地址:https://www.cnblogs.com/dangxw/p/3274897.html
Copyright © 2011-2022 走看看