Low latency Android & Arduino bluetooth -
i'm sampling audio on android, , sending rgb values based on arduino device using bluetooth.
there's long delay (several seconds) between audio samples being sent, , arduino reacting. i'm assuming caused android being faster arduino, , sort of flow control going on transmitting bytes getting backed buffer on phone. code connect bluetooth is:
mmsocket = mmdevice.createrfcommsockettoservicerecord(uuid); mmsocket.connect(); mmoutputstream = mmsocket.getoutputstream();
and send data:
mmoutputstream.write(0xff); mmoutputstream.write(outputfreq); mmoutputstream.write(outputmagnitude);
i don't mind losing data, need recent values sent.
what best way achive this? i'm new android programming work quite simpler solutions better! i've though sort of stack, , seperate thread runs on timer, , skims top of stack , sends values, sounds quite complex don't know thread programming.
is there way instead configure outputstream
discards data hasn't been sent in time?
here full code if it's help:
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 above arrays xyseries': plotseries = new simplexyseries( arrays.aslist(seriesdata), // simplexyseries takes list turn our array list simplexyseries.arrayformat.y_vals_only, // y_vals_only means use element index x value "series1"); // set display title of series // create formatter use drawing 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 new series' xyplot: plot.addseries(plotseries, series1format); // reduce number of range labels plot.setticksperrangelabel(3); // default, androidplot displays developer guides aid in laying out plot. // 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(); (int = 0; < 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 fast fourier transform 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 'samples' array , convert double[] audiorecord.read(buffer, 0, buffer.length); logtime("after reading"); (int = 0; < buffer.length; i++) { bufferd[i]=buffer[i]; } fft.realforward(bufferd); logtime("after fft"); integer[] spectrum = new integer[blocksize/2]; (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; (int = 0; < spectrum.length; i++) { averagemagnitude += spectrum[i]; } averagemagnitude /= spectrum.length; int halfmagnitudesum = 0; (int = 0; < spectrum.length / 2; i++) { halfmagnitudesum += spectrum[i] * i; } halfmagnitudesum /= 2; int runningtotal = 0; (int = 0; < 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 menu; adds items action bar if 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())); } } }
the outputstream.write();
lines returned immediately, amount of delay increased on time. data getting backed somewhere in bluetooth stack, why put in thread.sleep(10)
, try , slow things down.
that caused other problems around blocking though, , replaced couple of lines check when last write()
before sending new data. if less configured time (call timedelay
), skips new write()
. manually tuning value of timedelay
enabled me avoid flooding bluetooth stack. in
Comments
Post a Comment