android音頻發生器

這是前一段時間遇到的問題了, 想用android手機的音頻口發送消息,消息內容能夠是字符串,能夠是命令,甚至能夠是文件。因爲智能手機的普及,從此在工業領域可能會推廣,因此在這裏分享一下。php

如今開始:java

一(語文是硬傷,標題想不出來):android

本例只實現了發送字符串的功能(用的是方波標記),並且界面佈局很簡單,貼圖:ios

 

點擊【send】將會發送出一段噪音,噪音的內容是用你所輸入的字符串編碼過來的,用的是Ascall碼轉換成高低電頻,固然在手機的耳機插口須要插入一個本身開發的信號轉換器,接收端須要安裝配套的信號接受器,並且成本很低,彷佛是用單片機開發的。好吧,露餡了,我不懂硬件……api

 

 


綁定button。數組

 

 

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


listener的內容:ide

 

 

[java]  view plain copy
 
  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 plain copy
 
  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),那麼數組中包含的到底是什麼?測試

 

 

 

二:數組內容解析google

先來了解下耳機的構造:(耳機夠着此段轉載於http://blog.csdn.net/xl19862005/article/details/8522869

 

咱們知道,耳機是用來聽音樂,打電話的,既然是和聲音相關的,那麼耳機線上傳輸的就是音頻信號,常見的音頻信號通常都是在100Hz——10KHz左右的範圍內,那麼手機裏面的音頻輸出系統(DA和音頻功放)的幅頻特性(也既帶寬)必定也是在這個範圍(這是本人的猜測,因爲設備和儀器有限,沒有進行系統的測試,有興趣的朋友能夠用相關的測試儀器測測),那麼,既然有帶寬,好傢伙,咱們就能夠經過努力在這個頻帶內實現咱們的通訊信道了!另外值得提的一點是,耳機線上傳輸的音頻信號是交流的!

    下面咱們來看看市面上常見的耳機座(公頭)的引腳定義,android手機上用的耳機大多都是3.5mm的四芯座,在這四個芯中,分別是:地、左聲道、右聲道和線控開關(MIC)

一、國家標準


 

發現其實耳機接受的就是電流,那麼猜想那個數組中包含的應該就是電流的強度。那麼咱們只須要將所要發送的內容轉換爲電流的強度來標記,而且在接受端按照相同的方式來解碼便可。

三:audiosend類內容:

 

[java]  view plain copy
 
  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 plain copy
 
  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 plain copy
 
  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(雖然結果同樣)。具體我也不知道,應該是總監錯了,公司硬件還沒作好,過段時間才能測試,若是出錯了我再回來修改。
好了,終於寫完了。
相關文章
相關標籤/搜索