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

一、音频信号输出原理

音频耳机口输出信号的原理已经有大牛的文章,参考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. IE6不支持min-height或max-width等完美解决方法

    又是IE6!!!坑人的IE6,不支持min-height,但是实际操作中,这个属性是非常需要的.那IE6下面怎么实现呢?请看geniusalien提供的完美解决方案:(geniusalien温馨提示: ...

  2. 用PowerMock mock final类constructors

    也相对简单,直接贴代码 被测方法 public class EmployeeServiceWithParam { public void createEmployee(final Employee e ...

  3. 支付宝集成后报错ALI38173

    原因: 支付时传的参数不正确. 出现这个错误, 说明支付功能已经集成成功, 前后台核对下参数就能找到原因了.

  4. 一次线上http接口调用不通相关的解决过程

    2016-05-25 08:58:34 昨天线上小白系统因为调用外部http接口,超时不释放,导致页面反应很慢,时间一长,报502错误. 上网查了下,502错误是因为服务对于客户的请求没有得到及时的反 ...

  5. 【转】tomcat性能调优

    一.总结前一天的学习 从"第三天"的性能测试一节中,我们得知了决定性能测试的几个重要指标,它们是: ü   吞吐量 ü   Responsetime ü   Cpuload ü   ...

  6. git学习【转载】

    最近参与别人的github项目时,学习了Git的使用,首先需要在https://github.com/网站上注册账号和邮箱,然后fork一个开源项目,然后下载目前Windows下最新版本的git,下载 ...

  7. 如何通过JDBC访问数据库

    Java数据库连接(JDBC)用与在Java程序中实现数据库操作功能,它提供了执行SQL语句.访问各种数据库的方法,并为各种不同的数据库提供统一的操作接口,java.sql包中包含了JDBC操作数据库 ...

  8. sqoop笔记

    adoop学习笔记—18.Sqoop框架学习   一.Sqoop基础:连接关系型数据库与Hadoop的桥梁 1.1 Sqoop的基本概念 Hadoop正成为企业用于大数据分析的最热门选择,但想将你的数 ...

  9. SRS文档

    1什么是用例? 在介始用例方法之前,我们首先来看一下传统的需求表述方式-"软件需求规约"(Software Requirement Specification).传统的软件需求规约 ...

  10. VS2012新建项目出错:未找到与约束ContractName Microsoft.VisualStudio.Text.ITextDocumentFactoryService

    刚刚重新做的系统,第一次打开vs2012新建项目时出现错误提示 通过查找解决办法发现方法有两种: 1:卸载两个windows更新补丁(KB2833957和KB2840642) 2:安装一个window ...