1. 程式人生 > >安卓手把手教你學習並實現 安卓耳機口音訊轉紅外發射

安卓手把手教你學習並實現 安卓耳機口音訊轉紅外發射

安卓實現耳機口音訊轉紅外發射

前一段時間因為找工作,完了之後又有兩個專案做,一個 BLE4.0 的專案,一個紅外控制的專案,因此也好久沒寫文章了。BLE4.0 的資料網上一抓一大把,就不多說了。

雖說紅外很早就開始火了,從最早的遙控器,到紅外測距等等,但是網上關於 Android 紅外開發的相關資料幾乎沒有。那就只能硬著頭皮自己上。

手機自帶紅外有 ConsumerIrManager 類,很好用,略過。而我們今天看的是另一種紅外發送方式:音訊轉紅外

1、相關知識介紹:

這裡寫圖片描述 
這是網上找的格力空調的開機短碼,將這些數字理解成一個 TA 自己規定的協議,9000,4500 為幀頭,560,1690 代表 1,560,560 代表 0。先不管幀頭,剩下的翻譯過來就是{1,0,1,0 , 1,0,1,0,0,1,0,1,0,1,0,1},再翻譯為16進位制,即為0xAA,0x55,這是一個開機命令。

取樣率 44100:通俗理解就是 在1s內在一條連續的正玄波上面採集 44100 個點。 
載波 38KHZ: 即為我們發出的音訊訊號需要放在 38KHZ 的載波上才能傳送出去被紅外接收頭接收。

音訊轉紅外要做的就是生成 PCM(單/雙聲道)資料,即為音訊資料,按照硬體支援的 NEC 協議,指定取樣率,指定載波,使用 Android SDK 中的 audioTrack 類播放這段音訊即可

這裡寫圖片描述

注:多媒體基礎知識之PCM資料這裡面有 PCM 相關的知識,包括取樣率、載波等。

注:硬體自己焊接或者淘寶:android 音訊紅外發射頭,附焊接教程

2、音訊除錯:

音訊除錯我使用的電腦軟體 cooledit,百度一下就有免費的,再加一根3.5mm 的公對公耳機線。

這裡寫圖片描述這裡寫圖片描述

  • step1:使用遙控精靈搜到你的空調型號。

  • step2:公頭線一頭插耳機口,一頭插電腦音訊 mic 口,手機音量調到最大,並在電腦上開啟 cooledit 軟體。

  • step3:點選 cooledit 的錄音鍵,選擇取樣率 44100,雙聲道,16 位。開啟遙控精靈,打到你的空調按鈕面板,連續點選幾次開機鍵。

  • step4:可以看到 cooledit 軟體介面上有一些綠色的聲音波形。

這裡寫圖片描述

上面這張圖片是我抓的遙控精靈發出的電平訊號,可以看到 9ms 的高電平和 4.5ms 的低電平,雖然看到這兒是方波,但是再往後其實 TA 也是正玄波。高電平這兒全部是標準的正玄波組成的,寬度為時間寬度 9ms。

那對於我們來說只要仿造出圖中那樣的波形,9ms高、4.5ms低………….,即可和遙控精靈一樣控制我們的空調了。

3、仿造波形:

要仿造一段 20KHZ(因為耳機口只能輸出這麼大),取樣率 44100 , 16 位雙聲道的 PCM 音訊資料,網上還是有點資料可尋的。

目前,我沒有找到輸出方波的方法,再加上經過對遙控精靈輸出波形的觀察,也是輸出的正玄波,所以就放心的輸出 Sin 正玄波吧。

必要了解 ①:

 buffSize = AudioTrack.getMinBufferSize(this.sampleRate,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT) * 4;
  • 1
  • 2
  • 3

AudioTrack:音訊播放類。 
sampleRate:取樣率。 
AudioFormat.CHANNEL_OUT_STEREO:雙聲道輸出,即為立體聲,但同時也增加了檔案大小。 
AudioFormat.ENCODING_PCM_16BIT:16位,一個取樣點佔16位。但同時也增加了檔案大小。

必要了解 ②:

y(t) = A * sin (ωt + φ)

//ω即為角速度,在一段週期內轉過了多少角度。
//T為週期。
//f為頻率。
ω = 2π/T = 2πf  

y(t) = A * sin (2πft + φ)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

正玄波重要函式, 
- A: 振幅,這裡為1; 
- f : 頻率,這裡為 freqOfTone;(即為19000HZ) 
- t: 時間,這裡為 (i/sampleRate);(當i為44100時是不是就是1s了) 
- φ: 相位,這裡為0;

表示下來就是這樣:

  sample[i] = Math.sin(2 * Math.PI  * i * (freqOfTone /sampleRate));
  • 1

必要了解 ③:

 audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                    this.sampleRate, AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT, bytes1.length,
                    AudioTrack.MODE_STREAM);
  • 1
  • 2
  • 3
  • 4

STREAM_MUSIC:播放型別,有 Alerm、Notification 等。 
AudioTrack.MODE_STREAM:MODE 有 STREAM 和 STATIC 兩種。STREAM 型別意味著音訊可以被連續播放,只需要一直往緩衝池寫即可。STATIC 通常用於播放遊戲音等,適合短小音訊。 
bytes1.length:一次可播放音訊檔案的緩衝池大小。

核心程式碼片段:

       for (final double dVal : sample) {
          final short val = (short) ((dVal * 32767));
          final short val_minus = (short) -val;
          //左聲道
          generatedSnd[idx] = (byte) (val & 0x00ff);
          generatedSnd[idx+1] = (byte) ((val & 0xff00) >>> 8);
          //16位雙聲道  右聲道
          generatedSnd[idx+2] = (byte) (val_minus & 0x00ff);
          generatedSnd[idx+3] = (byte) ((val_minus & 0xff00) >>> 8);
          idx=idx+4;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

程式碼解釋:

上面第2行和第3行是將振幅縮放到最大振幅(32767是16位整數的最大值)。

上面第5、6行和第8、9行是填充PCM資料,上面有文章講了PCM資料格式,在16位wav PCM中,低位元組到高位元組:

樣本大小 資料格式 最小值 最大值
8位PCM int -128 127
16位PCM int -32768 32767

填充完畢後,我們就有了完整的正玄波資料。(即高電平正玄波)

        List<Byte> listByte = new ArrayList<>();
        for (int j = 0; j < patterns.length; j++) {
            int d=patterns[j];
            final int points = (int) ((((double) d / 1000000.0) * sampleRate)*4);
              if (j % 2 == 0) {
                for (int i = 0; i < points; i++) {
                    listByte.add(generatedSnd[i]);
                }
              } else {
                 for (int i = 0; i < points; i++) {
                     listByte.add((byte) 0);
                 }
              }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

程式碼解釋: 
patterns 即為我們要傳送的電平陣列,9000 , 4500… 那個。

看第4行,9000是 μs,9000/1000000,是將 μs 轉化為秒,再乘sampleRate 即為在9000μs 這段時間內佔有多少個取樣點。

因為9000為高電平,4500為低電平,再接下來又為高電平,然後又是低電平….所以偶數位為高電平 ,所以偶數位上這 points 個點都為高電平,到奇數位了這 points 個點都為低電平,低電平使用0表示即可。然後將所有的點拼裝到一起,組成完成的PCM資料,使用 AudioTrack 播放即可。

try {
            audioTrack.play();
        } catch (IllegalStateException e) {
            LogUtil.e( e.getMessage());
        }
           audioTrack.write(listByte, 0, listByte.length);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

格力空調的控制碼網上一搜一大堆,在這裡我不會開放原始碼,核心程式碼已經給出了,自己好好理解理解,分析分析,就可以自己寫出來了。

這個也是自己花了好長時間才搞定的,所以請尊重他人勞動成果,不做伸手黨。當然有不明白的可以在下面留言,歡迎交流,共同學習。

歡迎交流。