安卓手把手教你学习并实现 安卓耳机口音频转红外发射

来源:互联网 发布:淘宝电影票 编辑:程序博客网 时间:2024/04/28 12:58

安卓实现耳机口音频转红外发射

前一段时间因为找工作,完了之后又有两个项目做,一个 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,这是一个开机命令。

注:红外遥控器原理和NEC协议,这里面有相关的知识,建议先阅读。


采样率 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;

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 + φ)

正玄波重要函数,
- A: 振幅,这里为1;
- f : 频率,这里为 freqOfTone;(即为19000HZ)
- t: 时间,这里为 (i/sampleRate);(当i为44100时是不是就是1s了)
- φ: 相位,这里为0;

表示下来就是这样:

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

必要了解 ③:

 audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,                    this.sampleRate, AudioFormat.CHANNEL_OUT_STEREO,                    AudioFormat.ENCODING_PCM_16BIT, bytes1.length,                    AudioTrack.MODE_STREAM);

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;        }

代码解释:

上面第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);                 }              }        }

代码解释:
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);

格力空调的控制码网上一搜一大堆,在这里我不会开放源代码,核心代码已经给出了,自己好好理解理解,分析分析,就可以自己写出来了。

这个也是自己花了好长时间才搞定的,所以请尊重他人劳动成果,不做伸手党。当然有不明白的可以在下面留言,欢迎交流,共同学习。


my QQ : 1003077897
my csdn:http://blog.csdn.net/u012534831
my gay:https://github.com/qht1003077897

欢迎交流。

阅读全文
1 1