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(虽然结果一样)。具体我也不知道,应该是总监错了,公司硬件还没做好,过段时间才能测试,如果出错了我再回来修改。
    好了,终于写完了。
  • 相关阅读:
    二分练习题4 查找最接近的元素 题解
    二分练习题5 二分法求函数的零点 题解
    二分练习题3 查找小于x的最大元素 题解
    二分练习题2 查找大于等于x的最小元素 题解
    二分练习题1 查找元素 题解
    code forces 1176 D. Recover it!
    code forces 1173 B. Nauuo and Chess
    code forces 1173 C. Nauuo and Cards
    吴恩达深度学习课程笔记-15
    吴恩达深度学习课程笔记-14
  • 原文地址:https://www.cnblogs.com/dangxw/p/3274897.html
Copyright © 2011-2022 走看看