android PPM编码器音频库
我需要在android上实现音频PPM(脉冲位置调制) 参考: 我想从智能手机的音频输出中输出PPM。 最后一个范围是为无线电控制创建一个操纵杆。但是这个库可能有很多未来的用途(跟我来,lightbridge等等)。 收音机通常有PPM输出。发射机(和pc飞行模拟器)通常具有PPM输入。 我的目标是用安卓设备取代收音机。 我想知道是否有一些代码可以使用,或者我应该从头开始 编辑:我找到了一些开始点 1) smartpropplus是一款windows软件,可接收PPM音频并对其进行解码 2) 以下是PPM的结构: 3) 这是一幅简单的图像,解释了信号的结构: 我计算得出,在22000Hz下对音频信号进行采样足以实现每个通道的良好分辨率(每个通道22步) 注意:如果您对接收ppm音频信号感兴趣,您需要可以在这里找到的android ppm解码器类:我为ppm编码器类制作了一个工作示例。 我就是这样测试的: 1) 用PC记录产生的声音,我可以在“wavepad编辑器”上看到波形,它符合我们的需要 2) 使用pc记录智能手机的音频输出,并使用软件“SmartPropopus”及其调试实用程序分析音频信号,我可以使用android应用程序正确控制PPM通道 3) 我将手机连接到PPM接收器(DJI Lightbridge),但信号接收不正确。我怀疑信号电平不是dji设备预期的电平。我会等待你的反馈意见,但在那一刻之前,我怀疑我已经尽了我们在android上所能做到的最好android PPM编码器音频库,android,audio,remote-control,modulation,Android,Audio,Remote Control,Modulation,我需要在android上实现音频PPM(脉冲位置调制) 参考: 我想从智能手机的音频输出中输出PPM。 最后一个范围是为无线电控制创建一个操纵杆。但是这个库可能有很多未来的用途(跟我来,lightbridge等等)。 收音机通常有PPM输出。发射机(和pc飞行模拟器)通常具有PPM输入。 我的目标是用安卓设备取代收音机。 我想知道是否有一些代码可以使用,或者我应该从头开始 编辑:我找到了一些开始点 1) smartpropplus是一款windows软件,可接收PPM音频并对其进行解码 2)
注: 如果您想使用我的完整示例,您需要使用文件joystickView.jar,以便使用图形操纵手柄控制通道。 以下是如何使用它: 1) 从以下链接下载jar文件: 2) 在项目的根目录上创建一个名为“libs”的文件夹,并将JAR文件放入该文件夹中
现在你可以测试我的应用了 以下是我的测试应用程序的文件: 文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tr3ma.PPMtestProject"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.tr3ma.PPMtestProject.Test"
android:label="@string/app_name"
android:screenOrientation="landscape"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
文件PPMEncoder.java(这是针对原始问题请求的类)
包com.tr3ma.PPMtestProject;
导入java.util.ArrayList;
导入android.content.Context;
导入android.media.AudioFormat;
导入android.media.AudioManager;
导入android.media.AudioTrack;
导入android.os.AsyncTask;
公共类PPMEncoder
{
公共内部抽样率=44100;
public int ppmFrameBufferSize=(int)(采样率*0.0225);//22KHz*22,5ms表示它是帧ppm的持续时间
公共int音频缓冲区大小;
私有数组列表值;
AudioManager AudioManager;
StreamPPMSignalTask StreamPPMSignalTask;
私有布尔启动;
公共PPMEncoder(上下文)
{
audioManager=(audioManager)context.getSystemService(context.AUDIO\u服务);
//将音量设置为最大
//audioManager=(audioManager)context.getSystemService(context.AUDIO\u服务);
int tmpVol=audioManager.getStreamMaxVolume(audioManager.STREAM_MUSIC);
audioManager.setStreamVolume(audioManager.STREAM_MUSIC,tmpVol,0);
channelValues=新的ArrayList(8);
对于(int i=0;i<8;i++){
channelValues.add((浮点)0.68181818);
}
}
公共int开始生成()
{
试一试{
audioBufferSize=AudioTrack.getMinBufferSize(采样率,
AudioFormat.CHANNEL\u OUT\u MONO,
音频格式。编码\u PCM_16位)*2;
如果(audioBufferSizeHello!你成功了吗?我有Arduino的工作原型,我想修改Android的代码。由于我的Arduino代码可以工作,我可以将生成的wave与我的代码和你的代码的wave进行比较。wave看起来有些不同,但我不知道如何在你的代码中解决这个问题。@striker,我仍然没有收到我的新Android因为我的手机在音频输出上有硬件问题,所以我无法测试代码。这是arduino提供的反馈:这是Java,有一些重构,感谢smartpropoplus,我也可以安装它并进行调试。因此,旧代码也适用于smartpropoplus。新代码也适用。但在我的Turnigy上不适用于同样的问题:关闭Turnigy,设置频道音量,将audiojack连接到turnigy的培训师端口,打开菜单中的频道显示,我看到了变化!我从未使用过这种方式。你是否将智能手机连接到turnigy收音机?我有一个便宜的turnigy 9x,我可以用我的吗?你从哪里读取频道值?你在turnigy上写下了同样的问题,但你没有清除,因为您还写道,在turnigy显示屏上,您可以看到android appyeah接收到的频道。我使用minijack minijack电缆连接到turnigy9x。进入“菜单-功能设置-培训师”并激活频道7和8的培训师。在“菜单-功能设置-显示”中,您可以看到发射机频道值(移动支杆)。因此,我打开android应用程序,设置ch7,8值,用音频电缆连接我的tx,转到“菜单-显示”并查看频道上的值更改。如果我更改应用程序中的值,我看不到任何更改。如果我关闭tx,更改值并再次转到“菜单显示”,我会看到频道上的更改。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="true"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:id="@+id/stick1VerticalLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:text="stick1Vertical" />
<TextView
android:id="@+id/stick1HorizontalLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#ff0000"
android:ems="10"
android:text="stick1Horizontal" />
<TextView
android:id="@+id/stick2VerticalLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:text="stick2Vertical" />
<TextView
android:id="@+id/stick2HorizontalLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#ff0000"
android:layout_weight="1"
android:ems="10"
android:text="stick2Horizontal" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<com.zerokol.views.JoystickView
android:id="@+id/joystickViewLeft"
android:layout_width="wrap_content"
android:layout_height="fill_parent" />
<com.zerokol.views.JoystickView
android:id="@+id/joystickViewRight"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="end" />
</LinearLayout>
</LinearLayout>
package com.tr3ma.PPMtestProject;
import android.os.Bundle;
import android.widget.TextView;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import com.tr3ma.PPMtestProject.R;
import com.zerokol.views.JoystickView;
import com.zerokol.views.JoystickView.OnJoystickMoveListener;
public class Test extends Activity {
PPMEncoder ppmencoder;
private TextView stick1VerticalLabel;
private TextView stick1HorizontalLabel;
private TextView stick2VerticalLabel;
private TextView stick2HorizontalLabel;
// Importing as others views
private JoystickView joystickLeft;
private JoystickView joystickRight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
ppmencoder=new PPMEncoder(this);
//start the generation of the signal through the speakers
int result=ppmencoder.startGeneration();
if (result!=0){
//error occoured, something went wrong
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Error");
alert.setMessage("Error during audio signal generation. Error Number " + result);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
alert.show();
}
stick1VerticalLabel = (TextView) findViewById(R.id.stick1VerticalLabel);
stick1HorizontalLabel = (TextView) findViewById(R.id.stick1HorizontalLabel);
stick2VerticalLabel = (TextView) findViewById(R.id.stick2VerticalLabel);
stick2HorizontalLabel = (TextView) findViewById(R.id.stick2HorizontalLabel);
// referring as others views
joystickLeft = (JoystickView) findViewById(R.id.joystickViewLeft);
joystickRight = (JoystickView) findViewById(R.id.joystickViewRight);
// Listener of events, it'll return the angle in graus and power in percents
// return to the direction of the moviment
joystickLeft.setOnJoystickMoveListener(new OnJoystickMoveListener() {
@Override
public void onValueChanged(int angle, int power, int direction) {
//scompose the vector
float stickVertical=(float) Math.sin((Math.PI/180) * angle)*power; //values between +100 and -100
stickVertical=stickVertical+(float)100; //values between 0 and 200
stickVertical=(float)stickVertical*(float)((float)255/(float)200); //values between 0 and 255
float stickHorizontal=(float) Math.cos((Math.PI/180) * angle)*power; //values between +100 and -100
stickHorizontal=stickHorizontal+(float)100; //values between 0 and 200
stickHorizontal=stickHorizontal*(float)((float)255/(float)200); //values between 0 and 255
stick1VerticalLabel.setText("channel1:" + String.valueOf(stickVertical));
stick1HorizontalLabel.setText("channel2:" + String.valueOf(stickHorizontal));
ppmencoder.setChannelValue(1, stickVertical);
ppmencoder.setChannelValue(2, stickHorizontal);
}
}, JoystickView.DEFAULT_LOOP_INTERVAL);
joystickRight.setOnJoystickMoveListener(new OnJoystickMoveListener() {
@Override
public void onValueChanged(int angle, int power, int direction) {
//scompose the vector
//scompose the vector
float stickVertical=(float) Math.sin((Math.PI/180) * angle)*power; //values between +100 and -100
stickVertical=stickVertical+(float)100; //values between 0 and 200
stickVertical=(float)stickVertical*(float)((float)255/(float)200); //values between 0 and 255
float stickHorizontal=(float) Math.cos((Math.PI/180) * angle)*power; //values between +100 and -100
stickHorizontal=stickHorizontal+(float)100; //values between 0 and 200
stickHorizontal=stickHorizontal*(float)((float)255/(float)200); //values between 0 and 255
stick2VerticalLabel.setText("channel3:" + String.valueOf(stickVertical));
stick2HorizontalLabel.setText("channel4:" + String.valueOf(stickHorizontal));
ppmencoder.setChannelValue(3, stickVertical);
ppmencoder.setChannelValue(4, stickHorizontal);
}
}, JoystickView.DEFAULT_LOOP_INTERVAL);
}
@Override
protected void onDestroy() {
super.onDestroy();
int result=ppmencoder.stopGeneration();
if (result!=0){
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Error");
alert.setMessage("Error while stopping the audio generation. Error number " + result);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
alert.show();
}
}
}
package com.tr3ma.PPMtestProject;
import java.util.ArrayList;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.AsyncTask;
public class PPMEncoder
{
public int SAMPLE_RATE = 44100;
public int ppmFrameBufferSize = (int)(SAMPLE_RATE * 0.0225); // 22KHz * 22,5ms that it is the duration of a frame ppm
public int audioBufferSize;
private ArrayList<Float> channelValues;
AudioManager audioManager;
StreamPPMSignalTask streamPPMSignalTask;
private boolean started;
public PPMEncoder(Context context)
{
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
//set volume to max
//audioManager=(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int tmpVol = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, tmpVol, 0);
channelValues = new ArrayList<Float>(8);
for (int i = 0; i < 8; i++) {
channelValues.add((float)0.68181818);
}
}
public int startGeneration()
{
try {
audioBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT)*2;
if (audioBufferSize<=0 ) return -2;
started = true;
streamPPMSignalTask = new StreamPPMSignalTask();
streamPPMSignalTask.execute();
return 0;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public int stopGeneration()
{
try {
started = false;
streamPPMSignalTask.cancel(true);
streamPPMSignalTask = null;
return 0;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
private int timeToSamples(float time)
{
//time is expressed in milliseconds
return (int)Math.round(time * 0.001 * SAMPLE_RATE);
}
public void setChannelValue(int channel, float value)
{
channelValues.set(channel - 1, (float)0.68181818+(float)1.0 * ((float)value/(float)255));
}
public int setSamplingRate(int freq) {
//we can change the sampling frequency in case the default one is not supported
try {
SAMPLE_RATE=freq;
ppmFrameBufferSize = (int)(SAMPLE_RATE* 0.0225); // 22KHz * 22,5ms
audioBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT) * 2;
if (audioBufferSize<=0 ) return -2;
started=false;
stopGeneration();
startGeneration();
//frame=new byte[streamBufferSize];
return 0;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public class StreamPPMSignalTask extends AsyncTask<Void, Double, Void>
{
@Override
protected Void doInBackground(Void... arg0) {
AudioTrack ppmTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, audioBufferSize, AudioTrack.MODE_STREAM);
//set volume of audioplayer to max
ppmTrack.setStereoVolume((float) 1.0, (float) 1.0);
if (ppmTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
ppmTrack.play();
}
//feed the speakers with our audio, by continuously send the PPM frame
int tempBound;
while (started) {
try {
short[] frame = new short[ppmFrameBufferSize];
int i = 0;
tempBound = i + timeToSamples((float)0.3);
for (;i < tempBound; i += 1) {
frame[i] = Short.MIN_VALUE;
}
for (int channel = 0; channel < 8; channel++) {
tempBound = i + timeToSamples(channelValues.get(channel));
for (;i < tempBound; i += 1) {
frame[i] = Short.MAX_VALUE;
}
tempBound= i + timeToSamples((float)0.3);
for (;i < tempBound; i += 1) {
frame[i] = Short.MIN_VALUE;
}
}
for (;i < frame.length; i += 1) {
frame[i] = Short.MAX_VALUE;
}
//send the frame
ppmTrack.write(frame, 0, frame.length);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
}