Android 低延迟安卓&;Arduino蓝牙
我在安卓系统上采集音频,并基于此向使用蓝牙的arduino设备发送一些RGB值 在发送音频样本和arduino响应之间有很长的延迟(几秒钟)。我假设这是因为安卓系统比arduino快得多,而且传输字节被备份到手机的缓冲区中时,会进行某种流量控制。连接蓝牙的代码是:Android 低延迟安卓&;Arduino蓝牙,android,bluetooth,arduino,Android,Bluetooth,Arduino,我在安卓系统上采集音频,并基于此向使用蓝牙的arduino设备发送一些RGB值 在发送音频样本和arduino响应之间有很长的延迟(几秒钟)。我假设这是因为安卓系统比arduino快得多,而且传输字节被备份到手机的缓冲区中时,会进行某种流量控制。连接蓝牙的代码是: mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid); mmSocket.connect(); mmOutputStream = mmSocket.getOutputS
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
然后发送数据:
mmOutputStream.write(0xFF);
mmOutputStream.write(outputFreq);
mmOutputStream.write(outputMagnitude);
我不介意丢失数据,因为我只需要发送最新的值
实现这一目标的最佳方式是什么?我是Android编程新手,希望它能很快发挥作用,所以更简单的解决方案更好!我考虑过某种堆栈,以及一个独立的线程,它在计时器上运行,掠过堆栈顶部并只发送这些值,但这听起来相当复杂,因为我对线程编程一无所知
是否有办法将outputstream
配置为只丢弃未及时发送的数据
以下是完整的代码(如果有帮助):
package com.example.fft1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.androidplot.series.XYSeries;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.SimpleXYSeries;
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
import android.graphics.Color;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
XYPlot plot;
SimpleXYSeries plotSeries;
AudioRecord audioRecord;
RecordAudio recordTask;
int frequency = 44100;
int channelConfiguration = AudioFormat.CHANNEL_IN_MONO;
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
int counter;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize our XYPlot reference:
plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
plot.setRangeBoundaries(-1000000, 1000000, BoundaryMode.FIXED);
Number[] seriesData = {1,2,3,4,5,6};
// Turn the above arrays into XYSeries':
plotSeries = new SimpleXYSeries(
Arrays.asList(seriesData), // SimpleXYSeries takes a List so turn our array into a List
SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, // Y_VALS_ONLY means use the element index as the x value
"Series1"); // Set the display title of the series
// Create a formatter to use for drawing a series using LineAndPointRenderer:
@SuppressWarnings("deprecation")
LineAndPointFormatter series1Format = new LineAndPointFormatter(
Color.rgb(0, 200, 0), // line color
Color.rgb(0, 100, 0), // point color
null); // fill color (none)
// add a new series' to the xyplot:
plot.addSeries(plotSeries, series1Format);
// reduce the number of range labels
plot.setTicksPerRangeLabel(3);
// by default, AndroidPlot displays developer guides to aid in laying out your plot.
// To get rid of them call disableAllMarkup():
plot.disableAllMarkup();
Button startBtn = (Button)findViewById(R.id.startButton);
int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioRecord = new AudioRecord(
MediaRecorder.AudioSource.DEFAULT,
frequency,
channelConfiguration,
audioEncoding,
bufferSize
);
startBtn.setOnClickListener(new startBtnClick());
Button connectBtn = (Button)findViewById(R.id.connectBtn);
connectBtn.setOnClickListener(new connectBtnClick());
}
class startBtnClick implements Button.OnClickListener {
@Override
public void onClick(View view) {
Button button = (Button) view;
if (button.getText().toString().equals("Start")) {
button.setText("Stop");
recordTask = new RecordAudio();
recordTask.execute();
} else {
button.setText("Start");
recordTask.cancel(false);
}
}
}
class connectBtnClick implements Button.OnClickListener {
@Override
public void onClick(View view) {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(!mBluetoothAdapter.isEnabled()) {
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if(pairedDevices.size() > 0) {
for(BluetoothDevice device : pairedDevices) {
Log.v("BT2", "Device: " + device.getName());
if(device.getName().equals("linvor")) {
mmDevice = device;
break;
}
}
}
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); //Standard SerialPortService ID
try {
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
for (int i = 0; i < 255; i++) {
mmOutputStream.write(0xFF);
mmOutputStream.write(i);
mmOutputStream.write(255);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//beginListenForData();
}
private class RecordAudio extends AsyncTask<Void, Integer[], Void> {
@Override
protected Void doInBackground(Void... params) {
int blockSize = 128;
short[] buffer = new short[blockSize];
double[] bufferD = new double[blockSize];
audioRecord.startRecording();
// Here's the Fast Fourier Transform from JTransforms
DoubleFFT_1D fft = new DoubleFFT_1D(buffer.length);
while (!isCancelled()) {
counter = (counter + 1) % 1000;
//Log.v("FFT1", String.valueOf(counter));
int sumEnergy = 0;
logTime("start");
// Read audio to 'samples' array and convert it to double[]
audioRecord.read(buffer, 0, buffer.length);
logTime("after reading");
for (int i = 0; i < buffer.length; i++) {
bufferD[i]=buffer[i];
}
fft.realForward(bufferD);
logTime("after fft");
Integer[] spectrum = new Integer[blockSize/2];
for (int k = 0; k < blockSize / 2; k++) {
spectrum[k] = new Integer((int) Math.sqrt( (bufferD[2*k] * bufferD[2*k]) + (bufferD[2*k+1] * bufferD[2*k+1]) ));
}
int averageMagnitude = 0;
int middleFreqBin = 0;
for (int i = 0; i < spectrum.length; i++) {
averageMagnitude += spectrum[i];
}
averageMagnitude /= spectrum.length;
int halfMagnitudeSum = 0;
for (int i = 0; i < spectrum.length / 2; i++) {
halfMagnitudeSum += spectrum[i] * i;
}
halfMagnitudeSum /= 2;
int runningTotal = 0;
for (int i = 0; i < spectrum.length; i++) {
runningTotal += spectrum[i] * i;
if (runningTotal > halfMagnitudeSum) {
middleFreqBin = i;
break;
}
}
int outputMagnitude = map(averageMagnitude, 0, 50000, 0, 254);
int outputFreq = map(middleFreqBin, 0, spectrum.length, 0, 254);
if (outputMagnitude > 254) outputMagnitude = 254;
try {
//Log.v("FFT1", "OutputFreq: " + outputFreq + ", outputMagnitude: " + outputMagnitude);
mmOutputStream.write(0xFF);
mmOutputStream.write(outputFreq);
mmOutputStream.write(outputMagnitude);
Thread.sleep(10);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.v("FFT1","Not connected");
}
logTime("after bluetooth");
publishProgress(spectrum);
}
return null;
}
protected void onCancelled() {
audioRecord.stop();
}
protected void onProgressUpdate(Integer[]... args) {
Integer[] spectrum = args[0];
plotSeries.setModel(Arrays.asList(spectrum), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
plot.redraw();
}
int map(int x, int in_min, int in_max, int out_min, int out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void logTime(String text) {
if (counter < 5) {
String time = String.valueOf(new java.util.Date().getTime());
Log.v("FFT1", text + ": " + time.substring(time.length()-4, time.length()));
}
}
}
package com.example.fft1;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.OutputStream;
导入java.util.array;
导入java.util.List;
导入java.util.Set;
导入java.util.UUID;
导入com.androidplot.series.XYSeries;
导入com.androidplot.xy.BoundaryMode;
导入com.androidplot.xy.LineAndPointFormatter;
导入com.androidplot.xy.XYPlot;
导入com.androidplot.xy.SimpleXYSeries;
导入edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
导入android.graphics.Color;
导入android.media.AudioFormat;
导入android.media.AudioRecord;
导入android.media.MediaRecorder;
导入android.os.AsyncTask;
导入android.os.Bundle;
导入android.app.Activity;
导入android.bluetooth.BluetoothAdapter;
导入android.bluetooth.bluetooth设备;
导入android.bluetooth.BluetoothSocket;
导入android.content.Intent;
导入android.util.Log;
导入android.view.Menu;
导入android.view.view;
导入android.widget.Button;
导入android.widget.TextView;
公共类MainActivity扩展了活动{
XYPlot图;
Simplexyses绘图仪系列;
录音;
录音任务;
int频率=44100;
int channelConfiguration=单声道中的AudioFormat.CHANNEL\u;
int audioEncoding=AudioFormat.ENCODING_PCM_16位;
蓝牙适配器mBluetoothAdapter;
蓝牙插座;
蓝牙设备;
输出流mmOutputStream;
输入流mmInputStream;
整数计数器;
@抑制警告(“弃用”)
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化XYPlot引用:
plot=(XYPlot)findViewById(R.id.mySimpleXYPlot);
plot.SetRangeBounders(-1000000,1000000,BoundaryMode.FIXED);
数字[]系列数据={1,2,3,4,5,6};
//将上述阵列转换为XYSeries':
plotSeries=新的SimpleXYSeries(
asList(seriesData),//SimpleXYSeries接受一个列表,因此将数组转换为一个列表
SimpleXYSeries.ArrayFormat.Y\u VALS\u ONLY,//Y\u VALS\u ONLY表示使用元素索引作为x值
“序列1”);//设置序列的显示标题
//创建用于使用LineAndPointRenderer绘制系列的格式化程序:
@抑制警告(“弃用”)
LineAndPointFormatter系列1格式=新的LineAndPointFormatter(
rgb(0,200,0),//线条颜色
rgb(0,100,0),//点颜色
null);//填充颜色(无)
//向xyplot添加新系列:
plot.addSeries(plotSeries,series1Format);
//减少范围标签的数量
plot.setTicksPerRangeLabel(3);
//默认情况下,AndroidPlot显示开发人员指南,以帮助布置绘图。
//要清除它们,请调用disableAllMarkup():
plot.disablemarkup();
按钮开始按钮=(按钮)findViewById(R.id.startButton);
int bufferSize=AudioRecord.getMinBufferSize(频率、通道配置、音频编码);
音频记录=新的音频记录(
MediaRecorder.AudioSource.DEFAULT,
频率
信道配置,
音频编码,
缓冲区大小
);
setOnClickListener(新的startBtnClick());
按钮connectBtn=(按钮)findViewById(R.id.connectBtn);
setOnClickListener(新的connectBtnClick());
}
类StartBTClick实现Button.OnClickListener{
@凌驾
公共void onClick(视图){
按钮=(按钮)视图;
if(button.getText().toString().equals(“开始”)){
按钮。设置文本(“停止”);
recordTask=新建RecordAudio();
recordTask.execute();
}否则{
按钮.setText(“开始”);
recordTask.cancel(假);
}
}
}
类connectBtnClick实现Button.OnClickListener{
@凌驾
公共void onClick(视图){
mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
如果(!mBluetoothAdapter.isEnabled()){
意图启用Bluetooth=新意图(BluetoothAdapter.ACTION\u REQUEST\u ENABLE);
startActivityForResult(启用蓝牙,0);
}
设置pairedDevices=mBluetoothAdapter.getBondedDevices();
如果(pairedDevices.size()>0){
用于(蓝牙设备:pairedDevices){
Log.v(“BT2”,“设备:+Device.getName());
if(device.getName()