dimanche 19 avril 2015

android PPM audio library

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