這是前一段時間遇到的問題了, 想用android手機的音頻口發送消息,消息內容能夠是字符串,能夠是命令,甚至能夠是文件。因爲智能手機的普及,從此在工業領域可能會推廣,因此在這裏分享一下。php
如今開始:java
一(語文是硬傷,標題想不出來):android
本例只實現了發送字符串的功能(用的是方波標記),並且界面佈局很簡單,貼圖:ios
點擊【send】將會發送出一段噪音,噪音的內容是用你所輸入的字符串編碼過來的,用的是Ascall碼轉換成高低電頻,固然在手機的耳機插口須要插入一個本身開發的信號轉換器,接收端須要安裝配套的信號接受器,並且成本很低,彷佛是用單片機開發的。好吧,露餡了,我不懂硬件……api
綁定button。數組
- listener = new Listener();
- play = (Button)findViewById(R.id.play);
- text = (EditText)findViewById(R.id.text);
- play.setOnClickListener(listener);
listener的內容:ide
- private class Listener implements OnClickListener
- {
-
-
- @Override
- public void onClick(View arg0) {
-
- sendme = text.getText().toString();
- Log.d("!!!!!!","listener");
- if(sendme != ""&& sendme != null)
- {
- if(thread == null)
- {
- thread = new AudioThread();
- thread.start();
- }
- else
- thread.letRun();
- }
-
- }
-
- }
thread的內容:佈局
- class AudioThread extends Thread{
-
-
- @Override
- public void run() {
-
- while(true)
- {
- if(isrun)
- {
- Log.d("!!!!!!","runner");
- audioplayer = new AudioSend();
- audioplayer.play(sendme);
- try {
- sleep(1000);
- } catch (InterruptedException e) {
-
- e.printStackTrace();
- }
- isrun =false;
- }
-
- }
- }
- public void letRun()
- {
- Log.d("!!!!!!","letrunner");
- isrun = true;
- }
-
-
-
- }
已經發現重點就在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類內容:
- <span style="font-size:14px;">public class AudioSend {
- static int baudRate = 4800;
- static int maxRate = 48000;
- static int delayBit = 0;
- private static byte ihigh = (byte) (-128);
- private static byte ilow = (byte) (16);
-
- AudioTrack audioplayer;
- static int minSize;
-
-
- static byte[] getBuffer(String str)
- {
- int bytesinframe = delayBit + 10;
- byte[] sendme = str.getBytes();
- int n = maxRate/baudRate;
- boolean[] bits = new boolean[sendme.length * bytesinframe];
- byte[] waveform = new byte[(sendme.length*bytesinframe* n)];
-
- Arrays.fill(bits, true);
-
- int i,m,k,j = 0;
- for (i=0;i<sendme.length;++i)
- {
- m=i*bytesinframe;
- bits[m]=false;
- bits[++m]=((sendme[i]&1)==1);
- bits[++m]=((sendme[i]&2)==2);
- bits[++m]=((sendme[i]&4)==4);
- bits[++m]=((sendme[i]&8)==8);
- bits[++m]=((sendme[i]&16)==16);
- bits[++m]=((sendme[i]&32)==32);
- bits[++m]=((sendme[i]&64)==64);
- bits[++m]=((sendme[i]&128)==128);
-
- for(k=0;k<bytesinframe-9;k++)
- bits[++m]=true;
- }
-
-
- for (i=0;i<bits.length;i++)
- {
- for (k=0;k<n;k++)
- {
- waveform[j++]=(bits[i])?((byte) (ihigh)):((byte) (ilow));
-
- }
- }
-
-
- bits=null;
- return waveform;
- }
- public void play (String str)
- {
- byte[] send = getBuffer(str);
- minSize = AudioTrack.getMinBufferSize(4800,AudioFormat.CHANNEL_OUT_MONO
- ,AudioFormat.ENCODING_PCM_16BIT);
- audioplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 4800, AudioFormat.CHANNEL_OUT_MONO,
- AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM);
-
- audioplayer.play();
- audioplayer.write(send, 0, send.length);
- audioplayer.stop();
- audioplayer.release();
- }
-
- }
- </span>
在夠着audiotrack的時候
- <span style="font-size:14px;">minSize = AudioTrack.getMinBufferSize(4800,AudioFormat.CHANNEL_OUT_MONO
- ,AudioFormat.ENCODING_PCM_16BIT);
- audioplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 4800, AudioFormat.CHANNEL_OUT_MONO,
- 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碼,而後再將它轉換成對應的高低電流的波峯和波谷。
對於
- <span style="font-size:14px;">
- for (i=0;i<bits.length;i++)
- {
- for (k=0;k<n;k++)
- {
- waveform[j++]=(bits[i])?((byte) (ihigh)):((byte) (ilow));
-
- }
- }
- </span>
目的是爲了保真而延時,也就是將每一個波的變化延長k個單位時間,k呢是48000/4800得來的(其實這塊是總監要求的我不是很懂),我猜想,應該是android手機固定的是播放48000的採樣點,即使你定義成4800也必須本身作延時,可是不對啊,那還定義那個參數幹嗎?或者是單片機只能每秒接受480個點,也不對,由於那樣的話k應該等於4800/480而不是48000/4800(雖然結果同樣)。具體我也不知道,應該是總監錯了,公司硬件還沒作好,過段時間才能測試,若是出錯了我再回來修改。
好了,終於寫完了。