最近要做一个项目,利用手机的耳机口输出红外信号,从而把手机变成红外遥控器,信号处理的知识基本都还给老师了,刚开始真的挺头疼。找了不少资料研究了一下,总算有点心得,在这里做个备忘。

一、音频信号输出原理

音频耳机口输出信号的原理已经有大牛的文章,参考http://blog.csdn.net/xl19862005/article/details/8522869

再补充一点个人的理解,Android音频输出采样率一般为44.1kHz,AudioTrack源码中限制最大采样率为48kHz,也就是说耳机音频口输出的音频频率应该在20kHz左右,基本就是Android音频输出频率极限了。红外信号载波一般是38kHz,所以单纯的想通过音频信号是达不到要求的,需要借助外部硬件,找了一下,发现某宝上有的卖的,配套的app也有,下来试了一下,效果不错。不过既然是自己开发,需要搞清原理,反编译看了一下,核心代码都是native code,还是走正路,自己研究怎么实现。

网上找了一些相关资料,Linux平台下有相关的开源项目LIRC(Linux Infrared Remote Control):http://www.lirc.org/,支持各种类型的硬件,可以通过配置文件来支持各种类型的遥控设备,电视、dvd等等,是一个相对成熟的项目,目前google play中有很多红外遥控的应用都是基于此项目开发的。不过我这边只关注耳机口输出红外,LIRC中关于audio耳机口输出原理图(http://www.lirc.org/html/audio.html):

                    

利用耳机的左右声道,输出19kHz音频,通过左边的电路图,输出38kHz。

二、实现

原理搞清楚了,接下来实现,通过AudioTrack输出19kHz的正弦波形即可。这里没有将LIRC整体移植到android上,一来工程量太大,二来我只是用耳机这一种,其他的用不上,主要借鉴其中的音频输出原理。LIRC移植到Android可以参考开源项目irdroid:http://www.irdroid.com/

核心代码, 输出19kHz正弦,参考国外大牛:http://stackoverflow.com/questions/2413426/playing-an-arbitrary-tone-with-android

 public SignalProcessor(final int frequency) {
int buffSize = AudioTrack.getMinBufferSize(this.sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT) * 4; genSignal = new byte[buffSize];
genSpace = new byte[buffSize]; for (int j = 0; j < buffSize;) {
double dVal = Math.sin(2 * Math.PI * ((double)j)/4.0
/ (((double)sampleRate) / ((double)frequency)));
final short val = (short) ((dVal * 32767));
final short val_minus = (short) -val;
// in 16 bit wav PCM, first byte is the low order byte
genSpace[j] = 0;
genSignal[j++] = (byte) (val & 0x00ff);
genSpace[j] = 0;
genSignal[j++] = (byte) ((val & 0xff00) >>> 8);
genSpace[j] = 0;
genSignal[j++] = (byte) (val_minus & 0x00ff);
genSpace[j] = 0;
genSignal[j++] = (byte) ((val_minus & 0xff00) >>> 8);
} audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
this.sampleRate, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT, buffSize,
AudioTrack.MODE_STREAM);
audioTrack.play();
} public void play(final ArrayList<Integer> SignalSpaceList) {
boolean signal = true;
int count=0;
for (Integer d : SignalSpaceList) {
final int stop = (int) (((double) (d * sampleRate)) / 1000000.0) *4 ;
if (signal)
for (int i = 0; i < stop;)
{
if (stop - i < buffSize)
count= audioTrack.write(genSignal, 0, stop - i);
else
count = audioTrack.write(genSignal, 0, buffSize);
if(count>0)
i+=count;
}
else
for (int i = 0; i < stop;)
{
if (stop - i < buffSize)
count= audioTrack.write(genSpace, 0, stop - i);
else
count = audioTrack.write(genSpace, 0, buffSize);
if(count>0)
i+=count;
} signal = !signal;
}
}

解释一下:

 public void play(final ArrayList<Integer> SignalSpaceList)

传入信号与空闲时间的一个list,比如NEC编码中如图:

HEAD信号时间9ms,空闲时间4.5ms,list中传入9000,4500

测试耳机口单声道输出波形:

连接外设之后,输出方波:

Android使用AudioTrack发送红外信号的更多相关文章

  1. Android HTTP实例 发送请求和接收响应

    Android HTTP实例 发送请求和接收响应 Android Http连接 实例:发送请求和接收响应 添加权限 首先要在manifest中加上访问网络的权限: <manifest ... & ...

  2. Android - 向服务器发送数据(POST) - HTTPClient.

    该篇文章主要说明使用Apache提供的HTTPClient,通过post方式,向服务器发送数据.由于有些东西在 Android - 向服务器发送数据(GET)中提到过,就不再重复. 一,Android ...

  3. Android应用程序发送广播(sendBroadcast)的过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6744448 前面我们分析了Android应用程 ...

  4. android下获取无线wif信号、ssid、MAC等操作类

    一个android下获取无线wif信号.ssid.MAC等操作的类. WifiAdmin.java package com.afu; import java.util.List; import and ...

  5. Android广播的发送与接收

    Android广播的发送与接收 效果图 广播发送 广播分为有序广播和无序广播 有序广播与无序广播的区别 无序广播:只要是广播接收者指定了接收的事件类型,就可以接收到发送出来的广播消息.不能修改消息. ...

  6. 向 Nginx 主进程发送 USR1 信号

    [1]Nginx重新打开日志文件 向 Nginx 主进程发送 USR1 信号.USR1 信号是重新打开日志文件: 方式一: kill -USR1 $(cat /usr/local/lib/ubcsrv ...

  7. 如何实现PyQt5与QML响应彼此发送的信号?

    对于PyQt5+QML+Python3混合编程,如何实现PyQt5与QML响应彼此发送的信号,这是一个棘手的问题. 大抵有如下五种方式: (要运行下面五个例子,千万不能在eric6中运行,会报错.错误 ...

  8. Android向unity发送消息

    有些时候需要Android向unity发送消息,有两种方法实现,一.通过unity再带的消息机制,二.通过注册回调的方式. 一.通过UnityPlayer.UnitySendMessage():方法 ...

  9. 向nginx发送reopen信号以重新打开日志文件

    先移动日志文件 mv /usr/local/openresty/nginx/logs/access.log /usr/local/openresty/nginx/logs/access.log.201 ...

随机推荐

  1. jquery点击获取子元素ID值

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. sql中binary_checksum(*)的用法

    sql中binary_checksum(*)的用法(转) binary_checksum(*)可以用来检查修改过的行. 同一行在update后,该行的binary_checksum(*)就不同. 如 ...

  3. java语言程序设计(一)-2

    (一)jdk安装及环境变量配置 1.jdk下载 下载地址http://www.oracle.com/technetwork/java/javase/downloads/index.html,下载SE标 ...

  4. js对象的深度克隆

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Android 完整开源应用,完整开源项目

    (Antox)聊天的  (new)   (OpenKeychain)OpenPGP在android上的实现  (new)   (Flock)提供同步服务   (OpenFlappyBird)曾经火爆的 ...

  6. centos 6 SSH配置Google Authentication 验证

    创建工作目录: mkdir google-authentication 1. 安装二维码生成依赖 #wget http://fukuchi.org/works/qrencode/qrencode-3. ...

  7. 关于ajax跨域请求(cross Domain)

    Cross Domain AJAX主要就是A.com网站的页面发出一个XMLHttpRequest,这个Request的url是B.com,这样的请求是被禁止的,浏览器处于安全考虑不允许进行跨域访问, ...

  8. window上利用pip安装pandas

    官网推荐的是直接使用Anoconda,它集成了pandas,可以直接使用.安装挺简单的,有windows下的安装包.如果不想安装庞大的Anoconda,那就一步一步用pip来安装pandas.下面我主 ...

  9. MVC 支持同名路由,不同命名空间

    有时候我们会碰到两个项目合在一起,那么必然会碰到两个同名的controller,其实MVC在注册路由,添加Route的时候可以指定当前规则解析那个命名空间下的所有Controller. 注:Contr ...

  10. android系统自带的日期、时间对话框的用法

    代码: package com.test; import java.util.Calendar; import android.app.Activity; import android.app.Dat ...