I need to implement audio PPM (Pulse Position Modulation) on android
Reference: http://ift.tt/PdoAGi
I want to output PPM from the audio output of the smartphone. The final scope is to create a joystick for radiocontrol. but this library may have many future purposes (follow me, lightbridge, etc.etc.). The radios commonly have a PPM output. Transmitters (and pc flight simulators) commonly have PPM input. My scope is to replace the radio with an android device. I wish to know if there is some piece of code ready to use or should i start from scratch?
EDIT: I found some points where to start
1) smartpropplus is a windows software that receives PPM audio and decodes it http://ift.tt/1OTf5vR
2) this is how PPM is structured: http://ift.tt/1DSNF5u
3) this is a easy image that explains how the signal is structured: http://ift.tt/1DSNF5w
I calculated that sampling the audio signal at 22000Hz will be sufficient to achieve a good resolution for each channel (22 steps for each channel)
EDIT: I started to write the class, cause no one of you wrote a solution. actually this test application outputs a sine waveform frame. now it misses the functions to set the channels inside frame. i ear strange noise over the sine waveform, and i'm not sure which is the reason why:
this is the class PPMencoder
package com.tr3ma.PPMencoderProject;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.os.AsyncTask;
public class PPMencoder {
int samplingRate=44100;
//private int bufferSize ;
private int streamBufferSize = (int)(samplingRate* 0.0225); // 22KHz * 22,5ms
public AudioTrack audioPlayer ;
AudioManager audioManager;
private boolean started;
private byte[] frame;
public PPMencoder(Context context) {
//bufferSize= AudioRecord.getMinBufferSize(samplingRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
//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);
frame=new byte[streamBufferSize*2]; //doubled cause each sample is 1 integer
}
public int setSamplingRate(int freq) {
//we can change the sampling frequency in case it is not supported
samplingRate=freq;
//bufferSize = AudioRecord.getMinBufferSize(samplingRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
//if (!(bufferSize>0)){
// //the sampling frequency is not supported
// return -2;
//}
streamBufferSize = (int)(samplingRate* 0.0225); // 22KHz * 22,5ms
frame=new byte[streamBufferSize*2];
return 0;
}
public int startGeneration(){
try {
//if (!(bufferSize>0)){
// //the sampling frequency is not supported
// return -2;
//}
//start the loop that sends out the data continuously
started=true;
StreamAudio streamAudio;
streamAudio=new StreamAudio();
streamAudio.execute();
return 0;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
public int stopGeneration(){
try {
started=false;
if (audioPlayer==null) return 0;
audioPlayer.stop();
audioPlayer.flush();
audioPlayer.release();
return 0;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
public int setTestFrame() {
int tmpIndex=0;
float tmpVal;
//let's generate a sine at 2kHz
int sineFreq=2000;
for (int i = 0;i<frame.length;i=i+2){
tmpVal = (float) Math.sin( (float)tmpIndex * ((float)(2*Math.PI) * sineFreq / samplingRate)); //the part that makes this a sine wave....
short tmpVal1 = (short) (tmpVal * 32767);
frame[i] = (byte) (tmpVal1 & 0x00FF);
frame[i+1] = (byte) ((tmpVal1 & 0xFF00) >> 8);
tmpIndex=tmpIndex+1;
}
return 0;
}
public class StreamAudio extends AsyncTask<Void, Double, Void> {
@Override
protected Void doInBackground(Void... arg0) {
AudioTrack audioPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, samplingRate, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, frame.length, AudioTrack.MODE_STREAM);
//set volume of audioplayer to max
audioPlayer.setStereoVolume((float)1.0,(float)1.0);
if(audioPlayer.getPlayState() != AudioTrack.PLAYSTATE_PLAYING)
audioPlayer.play();
//feed the speakers with our audio, by continuously send the PPM frame
while (started) {
try {
//if ( audioPlayer.getState() != AudioTrack.STATE_INITIALIZED){
audioPlayer.write(frame, 0, frame.length);
//}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
}
and this is the activity class used to test the library:
package com.tr3ma.PPMencoderProject;
import android.os.Bundle;
import android.app.Activity;
import com.tr3ma.PPMencoderProject.R;
public class Test extends Activity {
PPMencoder ppmencoder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
ppmencoder=new PPMencoder(this);
//Just for test, let's feed the audio with a sine waveform
ppmencoder.setTestFrame();
//start the generation of the signal through the speakers
int result=ppmencoder.startGeneration();
if (result==-2){
//sampling frequency not allowed, you can ask the user to change it
}
if (result==-1){
//error occoured, something went wrong
}
}
@Override
protected void onDestroy() {
super.onDestroy();
int result=ppmencoder.stopGeneration();
if (result!=0){
//error occoured, something went wrong
}
}
}
and this is the manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://ift.tt/nIICcg"
package="com.tr3ma.PPMencoderProject"
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.PPMencoderProject.Test"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Aucun commentaire:
Enregistrer un commentaire